Skip to content
Advertisement

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("%lfn", 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?

Advertisement

Answer

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.

Advertisement