parseFloat of string longer than 16 characters - javascript

Does parseFloat of a string have a limit to how many characters the string can be? I don't see anything about a limit here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat
But running the following in console seems to show results I wasn't expecting.
parseFloat('1111111111111111'); // 16 characters long
// result 1111111111111111
parseFloat('11111111111111111'); // 17 characters long
// result 11111111111111112
Can anyone break this down for me?

In Javascript, floating point numbers are stored as double precision values. These have about 16 significant digits, which means that a 17-digit number won't necessarily be stored exactly.
You can supply numbers of any length to parseFloat(), but it won't be possible to store anything larger than 1.79769×10308, which is the largest possible value that can be stored in a double precision variable.
I'd recommend reading this if you have time: What Every Computer Scientist Should Know About Floating-Point Arithmetic

Related

Parsing amount with JSON.parse

Dos it exist a maximum float number with two digits after decimal point that can be parsed with JSON.parse without losing precision?
For example:
JSON.parse('{"amount": 9999999999999.99}')
{amount: 9999999999999.99} // not lose (probably)
JSON.parse('{"amount": 99999999999999.99}')
{amount: 99999999999999.98} // lose
If x is a decimal floating-point number with 15 significant digits or fewer, then converting x to JavaScript’s Number type and then converting the result back to a decimal floating-point number with the same number of significant digits produces exactly x, provided the number is within normal bounds. Therefore, all decimal numerals with two digits after the decimal point from “.00” to “9999999999999.99” can be parsed, stored, and reformatted with two digits after the decimal point, and the result will be the original numeral.
The stored value will generally not equal the original value. For example, when “.99” is parsed, the result will be exactly 0.9899999999999999911182158029987476766109466552734375. However, the stored value will be sufficiently close to the original value that, when converted back to the original number of digits, the original value is recovered. Note that you must know the original number of digits; it is not inherently a part of the Number value.
15 is a lower bound for this property. There may be some exponent values for which all 16-digit decimal numerals survive a round trip. However, since 99999999999999.99 (16 digits) produces 99999999999999.98, we know this is not one of those intervals.
If you want to know the specific number between 9999999999999.99 and 99999999999999.99 where this round-trip property first fails, you may have to hunt for it computationally. It many not be a value that is easy to calculate directly by mathematical properties.

Why does `btoa` encoding in javascript works for 20 digit string and not 20 digit int?

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=="
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.

How is the parseInt in JavaScript defined to handle large "numbers" - is there an ECMA leak? I got a wow here

The problem: JavaScript returns 20160107190308484 for parseInt("20160107190308485")
Not the question: I do not need a workaround; I got one.
Not the question II: I do not need an explanation why this can happen, and how numbers are stored and so on.
How are numbers as input for parseInt defined? Why do I get a wrong result, not a NaN?
I only find in documentation how parseInt handles literals and so on, that only the first numbers are interpreted and ...
As W3C or MDN
Note: Only the first number in the string is returned!
Note: Leading and trailing spaces are allowed.
Note: If the first character cannot be converted to a number, parseInt() returns NaN.
I can't find anything that says "123456789202232334234212312311" is not a "number". And nothing like "a number must be in the range of -BIGINT .. +BIGINT or so. And I could not find a hint for errors thrown.
I just get 20160107190308484 for "20160107190308485" (two browsers tested) and other pairs like:
20160107155044520, 20160107155044522
20160107002720970, 20160107002720967
20160107000953860, 20160107000953859
For me this feels like a hole in ECMA.
Is there a number defined that is allowed as input for parseInt?
For those who are interested in "how came?":
I stumbled over that problem with a small logic, that converts classes and ids into an object holding the information like
class="book240 lang-en" id="book240page2212"
converting to an object like:
{
book: 240
page: 2212
lang: "en"
}
Notice, that INTs are numbers, not strings.
Therefore I have a loop that makes that generic:
for n in some_regexp_check_names
# m hold a Regexp result arg[1] is the interest:
nr = parseInt(m[1]) # Make it a number
#--------- Here the funny fault
if nr > 0 # Check if number & > 0 all my ids are > 0
out_obj[n]=nr # Take the number
else
out_obj[n]=m[1] # Take the string as is
All this is nice and is working with "real strings" and "real (int) ids", and then I had a situation where dynamically semi uuids out of datetime where created: "temp-2016-01-07-19-03-08.485" - and still all fine, but I had the idea to remove all constant chars from this (i.e. temp-.) and BANG, because the string "20160107190308485" gives 20160107190308484 as parseInt.
And it took me only ~3 hours to find that error.
How are numbers as input for parseInt defined?
I think it's pretty clearly defined in the spec (§18.2.5):
Let mathInt be the mathematical integer value that is represented by Z
in radix-R notation, using the letters A-Z and a-z for digits with
values 10 through 35. (However, if R is 10 and Z contains more than 20
significant digits, every significant digit after the 20th may be
replaced by a 0 digit, at the option of the implementation; and if R
is not 2, 4, 8, 10, 16, or 32, then mathInt may be an
implementation-dependent approximation to the mathematical integer
value that is represented by Z in radix-R notation.)
Let number be the Number value for mathInt.
For that last step, the section for the Number type (§6.1.6) specifies:
In this specification, the phrase “the Number value for x” where x
represents an exact nonzero real mathematical quantity (which might
even be an irrational number such as π) means a Number value chosen in
the following manner. Consider the set of all finite values of the
Number type, with −0 removed and with two additional values added to
it that are not representable in the Number type, namely 21024 (which
is +1 × 253 × 2971) and −21024 (which is −1 × 253 × 2971). Choose the
member of this set that is closest in value to x. If two values of the
set are equally close, then the one with an even significand is
chosen; for this purpose, the two extra values 21024 and −21024 are
considered to have even significands. Finally, if 21024 was chosen,
replace it with +∞; if −21024 was chosen, replace it with −∞; if +0
was chosen, replace it with −0 if and only if x is less than zero; any
other chosen value is used unchanged. The result is the Number value
for x. (This procedure corresponds exactly to the behaviour of the
IEEE 754-2008 “round to nearest, ties to even” mode.)
Why do I get a wrong result not a NaN?
You get a rounded result (with the maximum precision available in that range) - and there's nothing wrong with that, your input is a valid number, not NaN.
Although Bergis answer is correct, I want to tell what my real mistake was:
Don't think of parseInt of a function, that parses into an INTeger like known from C as 16, 32 or 64 bit value. It parse the number as long as there are valid digits, so if the number is to big (does not fit exactly into eg 64 bit), you dont get an error, you just get a rounded value like working with float. If you want to be on the safe side: check the result against Number.MAX_SAFE_INTEGER.
The maximum integer value in JavaScript is 2^53 == 9 007 199 254 740 992. This is because Numbers are stored as floating point is a 52 bit mantissa.
Read more: http://web.archive.org/web/20161117214618/http://bjola.ca/coding/largest-integer-in-javascript/

Why is JavaScript's number *display* for large numbers inaccurate?

So in JavaScript, 111111111111111111111 == 111111111111111110000. Just type any long number – at least about 17 digits – to see it in action ;-)
That is because JavaScript uses double-precision floating-point numbers, and certain very long numeric literals can not be expressed accurately. Instead, those numbers get rounded to the nearest representable number possible. See e.g. What is JavaScript's highest integer value that a Number can go to without losing precision?
However, doing the math according to IEEE-754, I found out that every number inside [111111111111111106560, 111111111111111122944] will be replaced by 111111111111111114752 internally. But instead of showing this ...4752 number, it gets displayed as 111111111111111110000. So JavaScript is showing those trailing zeros which obfuscates the real behavior. This is especially annoying because even numbers like 263, which are accurately representable, get "rounded" as described.
So my question is: Why does JavaScript behave like this when displaying the number?
JavaScript integers can only be +/- 253, which is:
9007199254740992
One of your numbers is
111111111111111106560
which is considerably outside of the range of numbers that can accurately represented as an integer.
This follows the IEEE 754:
Sign bit: 1 bit
Exponent width: 11 bits
Significand precision: 53 bits (52 explicitly stored)
EDIT
The Display of numbers is sometimes rounded by the JavaScript engine, yes. However, that can be over-ridden using the toFixed method. (Warning, toFixed is known to be broken under some versions of IE).
In your console, type:
111111111111111122944..toFixed(0)
"111111111111111114752"

Parse Float has a rounding limit? How can I fix this?

I set up a system that parses a compact data string into JSON. I'm using a 19 digit number to store ids. Unfortunately any number greater than 17 digits, parseFloat() rounds the last few digits.
This breaks the whole data string. Can I fix this?
For example 8246295522085275215 gets turned into 8246295522085276000. Why is this?
http://jsfiddle.net/RobertWHurst/mhZ7Q/
JavaScript has only one numeric type, which is an IEEE 754 double precision floating-point. That means, you have a maximum of 52 bits of precision, which is a bit more than 15 decimal places.
If you need more precision than that, you have to use a bignum library or work with strings.
Numbers in JavaScript lose precision if they are higher than a certain value.
According to http://www.hunlock.com/blogs/The_Complete_Javascript_Number_Reference, integers are only reliable up to 15 digits (9 * 10^15 to be exact).
Try one of these
1. Use a string
2. Split your number in two and save the smaller parts to an array
3. Bignum library
4. Use a smaller number if you can

Categories