Why node.js v12.0.0 process 32+ bit numbers another way?

Viewed 84
$ nvm use 11.15.0 && node -e 'console.log(9876 ** 5 % 4321)' && node -p process.versions.v8
Now using node v11.15.0 (npm v6.7.0)
1341
7.0.276.38-node.19

$ nvm use 12.0.0 && node -e 'console.log(9876 ** 5 % 4321)' && node -p process.versions.v8
Now using node v12.0.0 (npm v6.9.0)
441
7.4.288.21-node.16

Whats changed, why node.js now process big numbers another way?

1 Answers

This has nothing to do with 32 bits as a limit/boundary of anything.

You can see the true result of 9876 ** 5 if you use BigInts: 9876n ** 5n === 93951865167752549376n That result has more bits than a 64-bit float, i.e. a JavaScript Number, can store. So when you use Numbers for the computation, you get: 9876 ** 5 === 93951865167752560000 Which is a value that's rounded to the maximum precision that a Number can store, which includes a rounding error.

In this commit, V8 changed how it computes exponentiation of doubles. The new implementation, based on fdlibm, is generally considered very good; in case of this particular computation it ends up rounding differently from the old implementation (which produced 9876 ** 5 === 93951865167752540000). I'm not sure whether any of them is generally better on average; at any rate the JavaScript spec allows "implementation-defined rounding".

Takeaway: when your Numbers get big, be aware that the last bits might be rounded off. For many use cases that doesn't matter, and floating-point behavior is a great fit. You just can't rely on % 4321 giving correct results. (Neither "1341" nor "441" is correct, they're just different rounding errors. The correct mathematical result of (9876 ** 5) % 4321 is 2140.) If you need bit-for-bit precision of large integers, use BigInts.

Related