As I see in examples, the functionality if ~~ and Math.floor is the same. Both of them round a number downward (Am I think correct?)
Also I should mention that according to this test ~~ is faster than Math.floor: jsperf.com/math-round-vs
So I want to know, is there any difference between ~~ and Math.floor?
Yes, bitwise operators generally don’t play well with negative numbers. f.ex:
~~-6.8 == -6 // doesn’t round down, simply removes the decimals
Math.floor(-6.8) == -7
And you also get 0 instead of NaN, f.ex:
~~'a' == 0
Math.floor('a') == NaN
In addition to David answer:
One of the things that I have noticed about bitwise operations in JavaScript is that it can be convenient for smaller values, but doesn’t always work for larger values. The reason this is the case is that bitwise operators will only work fully for operands which can be fully expressed in a 32-bit signed format. In other words, using bitwise operations will only produce numbers that are in the range of -2147483648 (-231) to 2147483647 (231 – 1). In addition, if one of the operands used is outside of that range, the last 32 bits of the number will be used instead of the specified number.
http://cwestblog.com/2011/07/27/limits-on-bitwise-operators-in-javascript/
This limitation can easily be found when working with Date, assume you are rounding a milliseconds value:
Math.floor(1559125440000.6) // 1559125440000
~~1559125440000.6 // 52311552
Related
I am student studying programming.
As far as I know, Javascript saves number as float.
However, bitwise operator in Javascript run as a type of number is integer.
For instance,
> 1 | 2 // -> 3
> 2 << 4 // -> 32
How is it possible?
I find official documentation(mdn web docs), but I can not find the reason.
The bitwise operators in JavaScript convert numbers to 32-bit integers before performing the operation. This means that even though JavaScript saves numbers as floats, the bitwise operators treat them as 32-bit integers. This is why you are able to use the bitwise operators on numbers and get integer results.
As #rviretural_001 mentioned, JavaScript does some automatic conversions by spec. For example, in addition to IEEE 754 doubles to 32 signed integers:
0 == '0' // true
String to Integer (Shift Right)
'4' >> 1 // 2
Converting to Integer (Bitwise OR)
'1.3' | 0 // 1
This last one was used in asm.js for 32 signed integers
just to contribute to the answers above note that the conversion of a floating-point number to an integer is done by removing the fractional part of the number.
For example, if you have the number 4.5 and you use a bitwise operator on it, JavaScript will convert it to the integer 4 before performing the operation.
This tutorial might help you to find out more https://www.programiz.com/javascript/bitwise-operators
Also note that performing bitwise operations on floating-point numbers can lead to unexpected results. This is due to the way floating-point numbers are represented in memory. So, it's recommended to avoid using bitwise operators on floating-point numbers in JavaScript.
Right shifting a number in javascript sometimes results in a negative number. What is the reason behind that? Can that be mitigated?
const now = 1562143596806 // UNIX timestamp in milliseconds
console.log(now >> 8) // -4783199
Use the zero-fill right shift operator (>>>) to always get a positive result:
const now = 1562143596806 // UNIX timestamp in milliseconds
console.log(now >>> 8)
The reason for the >> operator returning the number is caused by the fact that, originally, the number is internally represented as a 64-bit floating point number:
10110101110110111000000111010000100000110
The bit shift operation will first convert the operand to a 32-bit integer. It does this by keeping only the 32 least significant bits, and discarding the rest:
10110111000000111010000100000110
Then it will shift it by the specified number of bits while maintaining the sign, i.e. shifting in 8 1 bits from the left:
11111111101101110000001110100001
Converting back to decimal, this yields:
-4783199
The basic issue is that 1562143596806 is too large to fit in 32 bits. It can be represented as a Number, but when performing bitwise operations, the value is first converted to a 32bit integer and that means the "top bits" are already dropped before shifting - the upper bits of the result are therefore not filled from the original value, they are copies of the sign of that temporary 32bit value (or with >>>, they would be zero, which is not really an improvement). That the result happens to come out negative is just an accident depending on the exact bit pattern of the input, if it had been positive it would still have been the wrong positive value.
Such large values could be safely manipulated as BigInt, but support for that is lacking. Using floating point arithmetic can work, but requires extra care. For example you can divide by 256 and floor the result, but you cannot use the usual |0 to get rid of the fractional part, because even after dividing by 256 the value is too big to fit in 32 bits. Various non-built-in BigInt libraries exist to deal with this sort of thing too.
I just wanted to generate random integer number in range <-1,1>, that is -1, 0 or 1. I think this code nails it:
Math.round(Math.random()*2-1)
But I'm getting one more value apart from -1, 0 and 1. I'm not sure what is that supposed to be:
Now I don't think this is implementation error, but if it is, I'm using Firefox 41 and same thing happened in Google Chrome 39. When I tried to log -0 in console it appeared as -0 but -0 converted to String appears as "0". What is it? Can I use it for something, like some cool programming hacks and workarounds? Can it cause some errors if I don't just ignore it?
Two Zeros
Because JavaScript’s numbers keep magnitude and sign separate, each nonnegative number has a negative, including 0.
The rationale for this is that whenever you represent a number digitally, it can become so small that it is indistinguishable from 0, because the encoding is not precise enough to represent the difference. Then a signed zero allows you to record “from which direction” you approached zero; that is, what sign the number had before it was considered zero.
from here
More technical stuff if you are interested.
The implementation of -0 and +0 is introduced in 1985 by IEEE as part of the IEEE 754 standard. The standard addressed many problems found in the diverse floating point implementations that made them difficult to use reliably and portably.
The standard defines
arithmetic formats: sets of binary and decimal floating-point data, which consist of finite numbers (including signed zeros and subnormal numbers), infinities, and special "not a number" values (NaNs)
interchange formats: encodings (bit strings) that may be used to exchange floating-point data in an efficient and compact form
rounding rules: properties to be satisfied when rounding numbers during arithmetic and conversions
operations: arithmetic and other operations on arithmetic formats
exception handling: indications of exceptional conditions (such as division by zero, overflow, etc.)
ECMAScript has two zero numbers: +0 and -0.
They are considered equal by most comparison algorithms:
+0 == -0 // true (Equality comparison)
+0 === -0 // true (StrictEquality comparison)
[+0].includes(-0) // true (SameValueZero comparison)
However, they are different number values:
Object.is(+0, -0) // false (SameValue comparison)
Usually, both zeros behave identically in mathematical calculations. However,
1 / +0 === +Infinity
1 / -0 === -Infinity
In this case, you get -0 because Math.round is defined as follows:
If x is less than 0 but greater than or equal to -0.5, the result is
−0.
In general, if you have an integer between −231 and 231−1, and you want to convert it to +0 if it's -0, and don't alter it otherwise, you can use bitwise OR:
-1 | 0 // -1
-0 | 0 // +0
+0 | 0 // +0
+1 | 0 // +1
However, in your case you could just take the -1 outside of Math.round:
Math.round(Math.random()*2) - 1
But note using Math.round will produce a non-uniform probability distribution. Consider using
Math.floor(Math.random()*3) - 1
The function Math.round works as specified in the standard:
If x is less than 0 but greater than or equal to -0.5, the result is −0.
Math.random returns number between 0 (zero) and 1 (one) exclusively, so you will have several occurances of that rule.
I am not an expert in bitwise operators, but i often see a pattern which is used by programmers of 256k demos at competitions. Instead of using Math.floor() function, double bitwise NOT operator is used ~~ ( maybe faster ? ).
Like this:
Math.floor(2.1); // 2
~~2.1 // 2
Search revealed that there are more patterns that used the same way:
2.1 | 0 // 2
2.1 >> 0 // 2
When playing with this in the dev console, i have noticed a behavior that i'm not sure i understand fully.
Math.floor(2e+21); // 2e+21
~~2e+21; // -1119879168
2e+21 | 0; // -1119879168
What is happening under the hood ?
As Felix King pointed out, the numbers are being converted to 32 bit signed integers. 2e9 is less than the maximum positive value of a signed int, so this works:
~~(2e9) //2000000000
But when you go to 2e10, it can't use all the bits, so it just takes the lowest 32 bits and converts that to an int:
~~(2e10) //-1474836480
You can verify this by using another bitwise operator and confirming that it's grabbing the lowest 32 bits:
2e10 & 0xFFFFFFFF // also -1474836480
~~(2e10 & 0xFFFFFFFF) // also -1474836480
Math.floor is built to account for large numbers, so if accuracy over a big range is important then you should use it.
Also of note: The ~~ is doing truncation, which is the same as flooring for positive numbers only. It won't work for negatives:
Math.floor(-2.1) // -3
~~(-2.1) // -2
As stated in the MDN Docs, and here I quote,
The operands of all bitwise operators are converted to signed 32-bit integers in two's complement format.
This means tha when you apply a bitwise operator, for instance ~, to 2.1 it is first converted to an integer, and only then is the operator applied. This effectively achieves the rounding down (floor) effect for positive numbers.
As to why these operators are used, instead of the much nicer to grasp Math.floor, there are two main reasons. For one, these operators may be considerably faster to achieve the same result. Besides performance, some people just want the shortest code possible. All three operators you mentioned achieve the same effect, but ~~ just happens to be the shortest, and arguably the easiest to remember.
Given that the float to integer conversion happens before the bitwise operators are applied, let's see what happens with ~~. I'll represent our target number (2, after the convertion from 2.1) using 8 bits, instead of 32, for shortness.
2: 0000 0010
~2: 1111 1101 (-3)
~~2: 0000 0010
So, you see, we apply an operator to retrieve only the integer part, but we can't apply only one bitwise not, because it would mess up the result. We revert it to the desired value applying the second operator.
Regarding your last example, take into account that the number you're testing with, 2e+21, is a relatively big number. It's a 2 followed by twenty-one zeroes. It simply doesn't fit as a 32-bit integer (the data-type it is being converted to, when you apply the bitwise operators). Just look at the difference between your number and what a 32-bit signed integer can represent.
Max. Integer: 2147483647
2e+21: 2000000000000000000000
How about binary?
Max. Integer: 01111111111111111111111111111111
2e+21: 11011000110101110010011010110111000101110111101010000000000000000000000
Quite big, huh?
What really happens under the hood is that Javascript is truncating your big number to what it can represent in 32 bits.
110110001101011100100110101101110001011 10111101010000000000000000000000
^---- Junk ----^
When we convert our truncated number to decimal, we get back what you're seeing.
Bin: 10111101010000000000000000000000
Dec: -1119879168
Conversely, Math.floor accounts for the big numbers and avoids truncating them, which is one of the possible reasons for it being slower, although accurate.
I've recently discovered some other ways to remove the fractional part of numeric values in JavaScript other than Math.floor(n), specifically the double bitwise NOT operator ~~n and performing a bitwise or with 0 n|0.
I'd like to know what are the difference between these approaches and what the different scenarios are where one method is recommended over another.
The operands of all bitwise operators are converted to signed 32-bit integers:
Math.floor(2147483648) // 2147483648
2147483648 | 0 // 2147483648
~~2147483648 // 2147483648
Math.floor(2147483649) // 2147483649
2147483649 | 0 // -2147483647
~~2147483649 // -2147483647
So use Math.floor();
Be clear to the next person looking at your code and use Math.floor().
The performance gain of 1%-40% isn't really worth it, so don't make your code confusing and hard to maintain.
(I entirely agree with josh's answer: favor clear maintainable code.)
Here is an explanation on the other bit-wise approaches:
The bit-wise operators work because they only operator on 32-bit (signed) integers but numbers in JavaScript are all IEEE-754 values. Thus, there is an internal conversion (truncation, not floor!) that happens to operands for bit-wise operators.
The applied bit-wise operation (e.g. n<<0, ~~n or n|0) then acts as an identity function which "does nothing" to the converted values: that is, all of these approaches rely on the same conversion applied to bit-wise operands.
Try n as a negative number or a value outside of [-231, 231-1]:
(-1.23|0) // -1
Math.floor(-1.23) // -2
var x = Math.pow(2, 40) + .5
x|0 // 0
Math.floor(x) // 1099511627776
Happy coding.