That's immensely helpful. Thanks so much for your reply Jakob! One thing that I still found it a little hard to intuitively understand is why integers up to 2^52 can be represented exactly, while a number as seemingly simple as 0.10 can't be. After doing some examples, I think that the real problem comes from the fact that all of my intuition about numbers comes from working in base 10.
In reality, why 0.1 can't be represented exactly in binary but can be in decimal is the same as why 1/3 can't be represented exactly in decimal but can be in trinary (1.0 * 3^-1). Just because a fraction is "simple" in one base doesn't mean it is in another. Integers, however, can be perfectly represented by being split up into an exponent and significand in every base (given sufficient digits). Just like how, when you're multiplying by powers of 10, you just "slide" the decimal point to the right, the same logic applies in other bases. In binary: 1.01101 (binary) * 2^5 = 101101 = 13 (decimal) Notice how the dot just slides to the right, regardless of the base. Anyhow, just thought this was an intuitive way to think about it, so I thought I'd share. Thanks again for the help! On Tuesday, March 8, 2016 at 3:32:05 PM UTC-5, Jakob Kummerow wrote: > > TL;DR: No. > > As you point out, JavaScript dictates that all numbers be 64-bit doubles. > So V8 always behaves as if all numbers were stored as 64-bit doubles. (When > it doesn't, that's a bug.) > > Due to 64-bit floats having 52 bit mantissas, any integer up to 2^52 can > be represented exactly. In your example, ts1 and ts2 both fall into this > category. Accordingly, the result of "ts1 - ts2" will also be a perfectly > accurate integer. ts3, however, has more significant digits in its textual > representation than a 64-bit double can represent, so some digits will be > cut off no matter what. > > Due to the way floating-point numbers work, adding 438877949516 > + 10.472319812098124124462 will indeed cut off more digits in the result > than adding 16 + 10.472319812098124124462 will. However this has nothing to > do with V8. Java or C++ or any other language using IEEE754 doubles will > behave in the same way. > > Rule of thumb: the first ~15 ≈ log10(2^52) non-zero digits of the decimal > representation can be stored, the rest is cut off. So numbers that can be > represented exactly include e.g. 12345678901234500000 and 123456789012345 > and 12345678901234.5 and 12345.6789012345 and 0.0000123456789012345 etc. > A caveat is that choosing decimal vs. binary representation affects which > fractions can be represented with a finite number of digits; a common > example is 1/10 which is perfectly represented as "0.1" in decimal, but > cannot be represented exactly in any finite-precision binary number, much > like there is no finite-precision decimal representation of 1/3 = > 0.33333... . > > For *performance* reasons, V8 internally stores small integers as actual > integers. But from a functional point of view (i.e. what results you get > from any given arithmetic operation), this internal detail should never be > observable. > > > On Tue, Mar 8, 2016 at 8:05 PM, 'Charlie Andrews' via v8-users < > [email protected] <javascript:>> wrote: > >> *TL;DR* >> A friend mentioned that v8 might have accuracy optimizations built in for >> integer arithmetic. Is this true? >> >> *Long version:* >> With: >> >> var ts1 = 438877949516; >> var ts2 = 438877949500; >> var ts3 = 10.472319812098124124462; >> >> does >> >> (ts1 - ts2) + ts3 >> >> produce a result that's accurate to more digits than: >> >> (ts1 + ts3) - ts2 >> >> *The reasoning for why it might not be more accurate* >> Supposedly, all Javascript numbers are stored as 64 bit floating point >> numbers. 64 bit floats are generally stored according to IEEE 754-1985 >> <https://en.wikipedia.org/wiki/IEEE_754-1985>, which means they're >> broken up into 3 fields: a 1 bit sign, an 11 bit exponent, and a 52 bit >> significand. This means that the number 438877949516 is actually stored >> as >> >> sign exponent significand >> Binary 0 00000100101 >> 1.1001100010111100101010000011100100110000000000000000 >> Decimal 0 38 1.5966286792390747 >> >> You can get back to the original number with: >> >> +1 * 1.5966286792390747 * 2^38 ≈ 438877949516 >> >> (Note: this ignores bias in the exponent, which doesn't really matter for >> example purposes) >> >> A fixed number of bits in the significand (52) essentially means that you >> have a fixed number of binary significant figures. You can represent really >> really big numbers, but that means that you lose fractional accuracy. How >> many fractional binary significant figures you can provide is given by 52 - >> floor(log2(N)). In the case of 438877949516, this means that we dedicate 38 >> bits to representing the scale of the number and we can only dedicate 14 >> bits to fractional accuracy, so the smallest difference between two numbers >> as big as 438877949516 is 1/(2^14), or .00006104. >> >> This means that ts1 - ts2 (i.e. 438877949516 - 438877949500) gives us a >> number that's within .00006104 of 16. Add ts3 (10.472319812098124124462), >> and our significant figure rules tell us that we still only have a number >> within .00006104 of the true answer. >> >> *The reasoning for why it might be more accurate* >> If v8 has some optimization to do integer math exactly, then the story >> changes. ts1 - ts2 (i.e. 438877949516 - 438877949500) now gives us an >> exact answer of 16 with infinite fractional accuracy. We then add ts3 >> (10.472319812098124124462) >> and, because numbers around 10 have a fractional resolution of 1/2^(52 - >> floor(log2(10)) - about 1.7763568e-15 - we have a number that's about 10 >> orders of magnitude more accurate. >> >> Woo! Sorry for the long explanation, but this is complicated stuff. >> Insight is very much appreciated :) >> >> -- >> >> Charlie Andrews | Software Engineer | [email protected] <javascript:> >> >> >> -- >> -- >> v8-users mailing list >> [email protected] <javascript:> >> http://groups.google.com/group/v8-users >> --- >> You received this message because you are subscribed to the Google Groups >> "v8-users" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected] <javascript:>. >> For more options, visit https://groups.google.com/d/optout. >> > > -- -- v8-users mailing list [email protected] http://groups.google.com/group/v8-users --- You received this message because you are subscribed to the Google Groups "v8-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
