I made a copy of Math.abs(), using the two's complement to find the opposite of a number. The thing is that it's approximating some numbers, especially the ones with 1 to 3 decimal points.
function abs(x: number) {
return x & (1 << 63) ? ~x + 1 : x;
}
So it's checking the sign bit of the number, and if it is set, find the opposite.
It's approximating -214.45 to 214, or like -2745.892 to 2745, why is that?
Related
So I ran across a small piece of code that looks like this
Math.random() * 5 | 0 and was confused by what it did.
after some inspecting, it seems like the comparison turns the decimal into an integer. is that right? and so the piece of code is another way is saying give me a random number between 0 and 4. Can anyone explain why that is?
1) Math.random() function always return decimal value and will be less than one. Ex - 0.2131313
random()
Returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0.
2) Math.random()*5 will always be less than 5. (maxvalue - 4.99999).
3) The bitwise operator '|' will truncate the decimal values.
Edit : Paul is correct. '|' does more than just truncate.
But in this case Math.random()*5|0 - It truncates the decimal and returns the integar.
This question already has answers here:
Unfamiliar characters used in JavaScript encryption script
(3 answers)
Closed 8 years ago.
I am using a "LightenDarkenColor" function in my script and never really paid much attention to it until now and I noticed some operations and I had no idea what they were doing. I had actually never seen them before.
Those operators are >> and &. I also noticed that the function doesn't work in Firefox.
The function:
function LightenDarkenColor(color, percent) {
var num = parseInt(color,16),
amt = Math.round(2.55 * percent),
R = (num >> 16) + amt,
B = (num >> 8 & 0x00FF) + amt,
G = (num & 0x0000FF) + amt;
return (0x1000000 + (R<255?R<1?0:R:255)*0x10000 + (B<255?B<1?0:B:255)*0x100 + (G<255?G<1?0:G:255)).toString(16).slice(1);
}
What exactly are the operators and how do they work?
Imagine num is 227733 (= some mild dark green) and take
B = (num >> 8 & 0x00FF)
num >> 8 will shift the number (move digits) to the right by 2 hex digits (4 bits per digit x 2=8) making it become:
227733 => 002277
then & 0x00FF will clear out all digits except the last two
002277 => 000077
and that is the component for green.
Hex 00FF is binary 0000000011111111 and & (binary AND) is the operation that will compare all bit pairs one by one and set all bits to zero unless both operand bits are 1s. So ANDing to zeroes will lead to zeroes, and ANDing to ones will give as result the same digits of the other operand: 1 & 1 => 1, 0 & 1=>0. Ones remain ones, zeroes remain zeroes. So AnyNumber & 0000000011111111 = the right part (lower 2 digits) of AnyNumber.
It is just the standard way of getting a number subpart. In this case the green component. Shift right to clear the lower bits, and &0000...1111 to clear the upper bits.
After it got all color components, it adds amt to all of them (amt positive=lighter) and at the end it crops the values
R<255?R<1?0:R:255 means: if less then 0 use 0, if more than 255 use 255.
And finally restores the color as a single number (instead of *0x100 could have used R<<8 that is the opposite of >>8, instead of +, it could have used |, binary OR, to merge the components).
Note that the function uses B as the second component, assuming it is blue but in reality in RGB the second is Green. Yet the result is correct anyway (it would work whatever color components you used and however you named them)
They're bitwise operators.
>> is a bitwise (Sign-propagating) right shift,
& is a bitwise "and".
I could go into detail on what these operators do, but the MDN has a good explanation and example for each operator. It would be counter-productive to copy those.
What does the >> symbol mean? On this page, there's a line that looks like this:
var i = 0, l = this.length >> 0, curr;
It's bitwise shifting.
Let's take the number 7, which in binary is 0b00000111
7 << 1 shifts it one bit to the left, giving you 0b00001110, which is 14
Similarly, you can shift to the right: 7 >> 1 will cut off the last bit, giving you 0b00000011 which is 3.
[Edit]
In JavaScript, numbers are stored as floats. However, when shifting you need integer values, so using bit shifting on JavaScript values will convert it from float to integer.
In JavaScript, shifting by 0 bits will round the number down* (integer rounding) (Better phrased: it will convert the value to integer)
> a = 7.5;
7.5
> a >> 0
7
*: Unless the number is negative.
Sidenote: since JavaScript's integers are 32-bit, avoid using bitwise shifts unless you're absolutely sure that you're not going to use large numbers.
[Edit 2]
this.length >> 0 will also make a copy of the number, instead of taking a reference to it. Although I have no idea why anyone would want that.
Just like in many other languages >> operator (among << and >>>) is a bitwise shift.
I'm reading a tutorial on Perlin Noise, and I came across this function:
function IntNoise(32-bit integer: x)
x = (x<<13) ^ x;
return ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 7fffffff) / 1073741824.0);
end IntNoise function
While I do understand some parts of it, I really don't get what are (x<<13) and & 7fffffff supposed to mean (I see that it is a hex number, but what does it do?). Can someone help me translate this into JS? Also, normal integers are 32 bit in JS, on 32 bit computers, right?
It should work in JavaScript with minimal modifications:
function IntNoise(x) {
x = (x << 13) ^ x;
return (1 - ((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824);
}
The << operator is a bitwise left-shift, so << 13 means shift the number 13 bits to the left.
The & operator is a bitwise AND. Doing & 0x7fffffff on a signed 32-bit integer masks out the sign bit, ensuring that the result is always a positive number (or zero).
The way that JavaScript deals with numbers is a bit quirky, to say the least. All numbers are usually represented as IEEE-754 doubles, but... once you start using bitwise operators on a number then JavaScript will treat the operands as signed 32-bit integers for the duration of that calculation.
Here's a good explanation of how JavaScript deals with bitwise operations:
Bitwise Operators
x<<13 means shift x 13 steps to left (bitwise).
Furthermore a<<b is equivalent to a*2^b.
& 7ffffff means bitwise AND of leftside with 7FFFFFFF.
If you take a look at the bit pattern of 7FFFFFFF you will notice that the bit 32 is 0 and the rest of the bits are 1. This means that you will mask out bit 0-30 and drop bit 31.
A colleague of mine stumbled upon a method to floor float numbers using a bitwise or:
var a = 13.6 | 0; //a == 13
We were talking about it and wondering a few things.
How does it work? Our theory was that using such an operator casts the number to an integer, thus removing the fractional part
Does it have any advantages over doing Math.floor? Maybe it's a bit faster? (pun not intended)
Does it have any disadvantages? Maybe it doesn't work in some cases? Clarity is an obvious one, since we had to figure it out, and well, I'm writting this question.
Thanks.
How does it work? Our theory was that using such an operator casts the
number to an integer, thus removing the fractional part
All bitwise operations except unsigned right shift, >>>, work on signed 32-bit integers. So using bitwise operations will convert a float to an integer.
Does it have any advantages over doing Math.floor? Maybe it's a bit
faster? (pun not intended)
http://jsperf.com/or-vs-floor/2 seems slightly faster
Does it have any disadvantages? Maybe it doesn't work in some cases?
Clarity is an obvious one, since we had to figure it out, and well,
I'm writting this question.
Will not pass jsLint.
32-bit signed integers only
Odd Comparative behavior: Math.floor(NaN) === NaN, while (NaN | 0) === 0
This is truncation as opposed to flooring. Howard's answer is sort of correct; But I would add that Math.floor does exactly what it is supposed to with respect to negative numbers. Mathematically, that is what a floor is.
In the case you described above, the programmer was more interested in truncation or chopping the decimal completely off. Although, the syntax they used sort of obscures the fact that they are converting the float to an int.
In ECMAScript 6, the equivalent of |0 is Math.trunc, kind of I should say:
Returns the integral part of a number by removing any fractional digits. It just truncate the dot and the digits behind it, no matter whether the argument is a positive number or a negative number.
Math.trunc(13.37) // 13
Math.trunc(42.84) // 42
Math.trunc(0.123) // 0
Math.trunc(-0.123) // -0
Math.trunc("-1.123")// -1
Math.trunc(NaN) // NaN
Math.trunc("foo") // NaN
Math.trunc() // NaN
Javascript represents Number as Double Precision 64-bit Floating numbers.
Math.floor works with this in mind.
Bitwise operations work in 32bit signed integers. 32bit signed integers use first bit as negative signifier and the other 31 bits are the number. Because of this, the min and max number allowed 32bit signed numbers are -2,147,483,648 and 2147483647 (0x7FFFFFFFF), respectively.
So when you're doing | 0, you're essentially doing is & 0xFFFFFFFF. This means, any number that is represented as 0x80000000 (2147483648) or greater will return as a negative number.
For example:
// Safe
(2147483647.5918 & 0xFFFFFFFF) === 2147483647
(2147483647 & 0xFFFFFFFF) === 2147483647
(200.59082098 & 0xFFFFFFFF) === 200
(0X7FFFFFFF & 0xFFFFFFFF) === 0X7FFFFFFF
// Unsafe
(2147483648 & 0xFFFFFFFF) === -2147483648
(-2147483649 & 0xFFFFFFFF) === 2147483647
(0x80000000 & 0xFFFFFFFF) === -2147483648
(3000000000.5 & 0xFFFFFFFF) === -1294967296
Also. Bitwise operations don't "floor". They truncate, which is the same as saying, they round closest to 0. Once you go around to negative numbers, Math.floor rounds down while bitwise start rounding up.
As I said before, Math.floor is safer because it operates with 64bit floating numbers. Bitwise is faster, yes, but limited to 32bit signed scope.
To summarize:
Bitwise works the same if you work from 0 to 2147483647.
Bitwise is 1 number off if you work from -2147483647 to 0.
Bitwise is completely different for numbers less than -2147483648 and greater than 2147483647.
If you really want to tweak performance and use both:
function floor(n) {
if (n >= 0 && n < 0x80000000) {
return n & 0xFFFFFFFF;
}
if (n > -0x80000000 && n < 0) {
const bitFloored = n & 0xFFFFFFFF;
if (bitFloored === n) return n;
return bitFloored - 1;
}
return Math.floor(n);
}
Just to add Math.trunc works like bitwise operations. So you can do this:
function trunc(n) {
if (n > -0x80000000 && n < 0x80000000) {
return n & 0xFFFFFFFF;
}
return Math.trunc(n);
}
Your first point is correct. The number is cast to an integer and thus any decimal digits are removed. Please note, that Math.floor rounds to the next integer towards minus infinity and thus gives a different result when applied to negative numbers.
The specs say that it is converted to an integer:
Let lnum be ToInt32(lval).
Performance: this has been tested at jsperf before.
note: dead link to spec removed
var myNegInt = -1 * Math.pow(2, 32);
var myFloat = 0.010203040506070809;
var my64BitFloat = myNegInt - myFloat;
var trunc1 = my64BitFloat | 0;
var trunc2 = ~~my64BitFloat;
var trunc3 = my64BitFloat ^ 0;
var trunc4 = my64BitFloat - my64BitFloat % 1;
var trunc5 = parseInt(my64BitFloat);
var trunc6 = Math.floor(my64BitFloat);
console.info(my64BitFloat);
console.info(trunc1);
console.info(trunc2);
console.info(trunc3);
console.info(trunc4);
console.info(trunc5);
console.info(trunc6);
IMO: The question "How does it work?", "Does it have any advantages over doing Math.floor?", "Does it have any disadvantages?" pale in comparison to "Is it at all logical to use it for this purpose?"
I think, before you try to get clever with your code, you may want to run these. My advice; just move along, there is nothing to see here. Using bitwise to save a few operations and having that matter to you at all, usually means your code architecture needs work. As far as why it may work sometimes, well a stopped clock is accurate twice a day, that does not make it useful. These operators have their uses, but not in this context.