Skip to content

Instantly share code, notes, and snippets.

@lsloan
Last active April 13, 2021 17:46
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lsloan/f8c5ab552545ee968cca to your computer and use it in GitHub Desktop.
Save lsloan/f8c5ab552545ee968cca to your computer and use it in GitHub Desktop.
JavaScript floating point math bug example
/*
* Demonstrate JavaScript floating point math bugs by showing
* which two-decimal-place numbers between 0.00 and 1.00 inclusive
* have fractional parts after being multiplied by one hundred.
*/
var i = 0.00;
for (n = 0; n <= 100; ++n) {
j = i * 100;
if (Math.round(j) != j) {
console.log(n, 'Multiplication error:', i, j);
}
i += 0.01;
k = i.toPrecision(4);
if (k != i) {
console.log(n, 'Addition error:', k, i);
i = +k;
}
}
// Results:
/*
5 "Addition error:" "0.06000" 0.060000000000000005
6 "Addition error:" "0.07000" 0.06999999999999999
7 "Multiplication error:" 0.07 7.000000000000001
9 "Addition error:" "0.1000" 0.09999999999999999
14 "Multiplication error:" 0.14 14.000000000000002
14 "Addition error:" "0.1500" 0.15000000000000002
17 "Addition error:" "0.1800" 0.18000000000000002
20 "Addition error:" "0.2100" 0.21000000000000002
23 "Addition error:" "0.2400" 0.24000000000000002
28 "Multiplication error:" 0.28 28.000000000000004
28 "Addition error:" "0.2900" 0.29000000000000004
29 "Multiplication error:" 0.29 28.999999999999996
34 "Addition error:" "0.3500" 0.35000000000000003
40 "Addition error:" "0.4100" 0.41000000000000003
46 "Addition error:" "0.4700" 0.47000000000000003
55 "Multiplication error:" 0.55 55.00000000000001
56 "Multiplication error:" 0.56 56.00000000000001
56 "Addition error:" "0.5700" 0.5700000000000001
57 "Multiplication error:" 0.57 56.99999999999999
58 "Multiplication error:" 0.58 57.99999999999999
68 "Addition error:" "0.6900" 0.6900000000000001
81 "Addition error:" "0.8200" 0.8200000000000001
93 "Addition error:" "0.9400" 0.9400000000000001
*/

Demonstrate JavaScript floating point math bugs by showing which two-decimal-place numbers between 0.00 and 1.00 inclusive have fractional parts after being multiplied by one hundred.

The original version of this program used the floating-point value, i, as the loop counter of the for-loop. Apparently, using a floating-point number, along with a loop final-expression that converted the value to a string and back to a float again in an attempt to carefully increment by 0.01, caused Bad Things™ to happen. E.g., the loop would run for thousands of iterations rather than exactly one hundred, causing the browser to freeze.

The original code:

for (i = 0.00; i <= 1.00; i = +((i + 0.01).toPrecision(2))) {
    // Note the final-expression in the for-loop limits decimal places,
    // overcoming float errors from adding 0.01
    j = i * 100;
    if (Math.round(j) != j) {
        console.log('Multiplcation error:', i, j);
    }
}

Changed in the new version:

  • Added an integer loop counter, n
  • Check for floating point addition errors, and if found
    • Display a message
    • Convert the more-precise number from a string back to a float

The last point may be significant, because it avoids converting the float to a string and back to a float again in every iteration. This conversion is done only when necessary.

/*
* Another version in which the variable "i" is actually a string,
* avoiding bugs in floating point addition, at the cost of possibly
* more type conversions.
*/
for (n = 0; n <= 100; ++n) {
with ('00' + n) {
// i = (n / 100), two decimal places
i = substr(0, (length-2)) + '.' + substr(-2);
}
j = i * 100;
if (Math.round(j) != j) {
console.log(n, 'Multiplication error:', i, j);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment