Why does `btoa` encoding in javascript works for 2

2019-08-09 17:05发布

问题:

Consider the following btoa output

btoa(99999999999999999999)
"MTAwMDAwMDAwMDAwMDAwMDAwMDAw"

btoa(99999999999999999998)
"MTAwMDAwMDAwMDAwMDAwMDAwMDAw"

btoa("99999999999999999998")
"OTk5OTk5OTk5OTk5OTk5OTk5OTg="

btoa("99999999999999999999")
"OTk5OTk5OTk5OTk5OTk5OTk5OTk="

We see that btoa is unable to encode unique hash for 20 digit int but was able to encode 20 digit string. Why is this?

My original guess is that since btoa is base 64 it can only encode something that is less than base 64, but why is it capable of encoding a 20 digit string instead?

Moreover btoa seems to not able to encode int less than 9223372036854775807 which is a 2^63

btoa(9223372036854775302)
"OTIyMzM3MjAzNjg1NDc3NjAwMA=="

btoa(9223372036854775303)
"OTIyMzM3MjAzNjg1NDc3NjAwMA=="

回答1:

Because of floating point imprecision. Remember, JavaScript doesn't have integers except temporarily during certain calculations; all JavaScript numbers are IEEE-754 double-precision floating point. 99999999999999999999 is not a safe integer value in IEEE-754 numbers, and in fact if you do this:

console.log(99999999999999999999);

...you'll see

100000000000000000000

The max safe integer (e.g., integer that won't be affected by floating point imprecision) is 9007199254740991.

Since btoa accepts a string (when you pass it a number, the number just gets converted to string), just put quotes around your value:

btoa("99999999999999999999")
=> OTk5OTk5OTk5OTk5OTk5OTk5OTk=

Of course, if the value is the result of a math calculation, you can't do that. You'll have to change whatever it is that's calculating such large numbers, as they exceed the precise range of the number type.