Search code examples
javascriptcfloating-pointfloating-accuracy

JavaScript seems to be doing floating point wrong (compared to C)


From everything I've been able to find online, JavaScript allegedly uses IEEE 754 doubles for its numbers, but I have found numbers that can work in C doubles, but not in JavaScript. For example,

#include <stdio.h>

int main(){
    double x = 131621703842267136.;
    printf("%lf\n", x);
}

prints 131621703842267136.000000 NOTE: IN AN EARLIER VERSION OF THE QUESTION I COPPIED THE WRONG NUMBER FOR C, but in JavaScript

console.log(131621703842267136)

outputs 131621703842267140. From everything I've read online, both C doubles and JavaScript numbers are 64-bit floating point, so I am very confused why they would output different results. Any ideas?


Solution

  • JavaScript’s default conversion of a Number to a string produces just enough decimal digits to uniquely distinguish the Number. (This arises out of step 5 in clause 7.1.12.1 of the ECMAScript 2018 Language Specification, which I explain a little here.) Formatting via console.log is not covered by the ECMAScript specification, but likely the Number is converted to a string using the same rules as for NumberToString.

    Since stopping at the ten’s digit, producing 131621703842267140, is enough to distinguish the floating-point number from its two neighboring representable values, 131621703842267120 and 131621703842267152, JavaScript stops there.

    You can request more digits with toPrecision; the following produces “131621703842267136.000”:

    var x = 131621703842267136;
    console.log(x.toPrecision(21))

    (Note that 131621703842267136 is exactly representable in IEEE-754 basic 64-bit binary format, which JavaScript uses for Number, and many C implementations use for double. So there are no rounding errors in this question due to the floating-point format. All changes result from conversions between decimal and floating-point.)

    Prior to an edit at 2019-05-17 16:27:53 UTC, the question stated that a C program was showing “131621703737409536.000000” for 131621703842267136. That would not have been conforming to the C standard. The C standard is lax about its floating-point formatting requirements, but producing “131621703737409536.000000” for 131621703842267136 violates them. This is governed by this sentence in C 2018 (and 2011) 7.21.6.1 13:

    Otherwise, the source value is bounded by two adjacent decimal strings L < U, both having DECIMAL_DIG significant digits; the value of the resultant decimal string D should satisfy LDU, with the extra stipulation that the error should have a correct sign for the current rounding direction.

    DECIMAL_DIG must be at least ten, by 5.2.4.2.2 12. The number 131621703842267136 (bold marks the tenth digit) is bounded by the two adjacent ten-digit strings “131621703800000000” and “131621703900000000”. The string “131621703737409536.000000” is not between these.

    This also cannot be a result of the C implementation using a different floating-point format for double, as 5.2.4.2.2 requires the format be sufficient to convert at least ten decimal digits to double and back to decimal without change to the value.