L
L
lil_web2020-04-17 22:21:53
JavaScript
lil_web, 2020-04-17 22:21:53

Why does XOR 0 remove the fractional part of the number?

console.log( 9.9 ^ 0); // 9
console.log( 999.99 ^ 0); // 999


Why ^ 0makes a number an integer? How it works? Is this a better way Math.floor(9.9)or parseInt(9.9, 10)?

Answer the question

In order to leave comments, you need to log in

2 answer(s)
D
Dmitry Belyaev, 2020-04-18
@lil_web

Stranger in the Q already explained why the number turns out to be an integer.
I will try to explain how it works, and why it is not better than Math.floor:
In js, the number type is represented by double precision numbers according to the IEEE 754 coding standard, which means that 53 bits are allocated for the significant part (highest bit - sign bit, if 1 - then the number is negative) and 11 bits for the exponent (highest bit, again, sign bit), a total of 64 bits. Moreover, this standard allows you to absolutely accurately work with integers up to ±2 52 , and therefore, until recently, there was no honest int in js.
Now to binary (I don't like the translation "bitwise") operations, as you know, "exclusive or" (xor, ^) with zero, as well as "or" (or, |) with zero do not change the number, returning another operand unchanged form. But here the js standard intervenes and says that binary operations are performed on a signed int32 representation of a number, and when presented, you can simply discard all that is superfluous, which engines do.
How to discard all unnecessary? It’s simple, the js engine knows perfectly well in which part of the number the significant part is located, it simply takes the lower 32 bits of the significant part and puts them in an int32 container and also transfers the sign bit to its place. The reverse transformation is similar - we decompose the container into a zero float64 container: the most significant bit (sign) in its place, the remaining 31 bits in its own place.
Now let's experiment in a regular browser console:

Math.floor(1.1) // 1
parseInt(1.1) // 1
1.1 | 0 // 1
1.1 ^ 0 // 1 - как уже писал выше, эффект будет 1 в 1, как и с |

// пока все было ок, но как насчет отрицательных чисел?
Math.floor(-1.1) // -2 - округляем в сторону -Infinity
parseInt(-1.1) // -1 - отбрасываем экспоненту
-1.1 | 0 // -1 - отбрасываем экспоненту и не только (но тут не имеет значения пока)

// попробуем с чем-нибудь побольше
2 ** 31 // 2147483648
2147483648 | 0 // -2147483648
/*
не зная как преобразовывается число выше, это было бы не очевидно
но смотрим выше "он просто берет младшие 32 бита значимой части и кладет их в int32 контейнер"
2 ** 31 в двоичном виде - это единичка и 31 нолик
и эта единичка попала в бит знака при переноси в int32
тем самым мы получили самое маленькое число в int32
*/

// давайте еще пример, набью как я большое число от балды:
34646456436346346212424 // 3.464645643634635e+22
34646456436346346212424 | 0 // 1178599424
/*
ну в первом примере мы видим интересную особенность чисел с плавающей запятой
мы можем хранить не только дробные числа, но и очень большие целые, правда с потерей точности
e+22 - это и есть экспонента, это значит число до e нужно умножить на основание системы счисления (10) в степени +22
а при бинарной операции эта экспонента отбрасывается, как и старшие биты значимой части
в 32 младших битах осталось 1178599424
забавно, что я случайно вбил такое число, у которого 32 бит содержал 0 (шансы были 50/50)
если бы там оказалась 1, то я бы вновь получил отрицательное число
*/

D
Dmitry Makarov, 2020-04-18
@MDiMaI666

The best way
let v = 999.99|0; // out: 999

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question