1

I'm trying to print out the integers from 11 to 30 multiplied by 0.015

for ( let i = 11; i <= 30; i++ ) {
    console.log( `${i} * 0.015 = ${i * 0.015}` );
}

I expect the output to look like this:

11 * 0.015 = 0.165
12 * 0.015 = 0.18
13 * 0.015 = 0.195
14 * 0.015 = 0.21
15 * 0.015 = 0.225
16 * 0.015 = 0.24
17 * 0.015 = 0.255
18 * 0.015 = 0.27
19 * 0.015 = 0.285
20 * 0.015 = 0.3
21 * 0.015 = 0.315
22 * 0.015 = 0.33
23 * 0.015 = 0.345
24 * 0.015 = 0.36
25 * 0.015 = 0.375
26 * 0.015 = 0.39
27 * 0.015 = 0.405
28 * 0.015 = 0.42
29 * 0.015 = 0.435
30 * 0.015 = 0.45

Instead I get this:

11 * 0.015 = 0.16499999999999998
12 * 0.015 = 0.18
13 * 0.015 = 0.195
14 * 0.015 = 0.21
15 * 0.015 = 0.22499999999999998
16 * 0.015 = 0.24
17 * 0.015 = 0.255
18 * 0.015 = 0.27
19 * 0.015 = 0.285
20 * 0.015 = 0.3
21 * 0.015 = 0.315
22 * 0.015 = 0.32999999999999996
23 * 0.015 = 0.345
24 * 0.015 = 0.36
25 * 0.015 = 0.375
26 * 0.015 = 0.39
27 * 0.015 = 0.40499999999999997
28 * 0.015 = 0.42
29 * 0.015 = 0.435
30 * 0.015 = 0.44999999999999996

I looked around on StackOverflow and it seems like JavaScript has an issue with floating point precision. So I tried one of the methods I saw for rounding numbers to an arbitrary step:

function round( number, step ) {
    const inverseStep = 1 / step;
    return Math.round( number * inverseStep ) / inverseStep;
}

for ( let i = 11; i <= 30; i++ ) {
    const rounded = round( i * 0.015, 0.015 );
    console.log( `${i} * 0.015 = ${rounded}` );
}

And I still get the same results with the strange irrational looking decimals.

griswold88
  • 11
  • 1
  • 7
    It's not JavaScript, it's *every* programming language that uses modern binary floating point. – Pointy Aug 28 '19 at 02:34
  • It's not JS, this applies to every programming language. Check this out https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html – Stanley Nguyen Aug 28 '19 at 02:36

5 Answers5

1

Floating point precision is good enough to get the job done, you just need to round it. You can use

.toFixed(2) 

to round your result to two decimal places.

Note that this converts your result to a string (which is fine for what you're doing) so if you convert back to a floating point the inaccuracy will return. If you are actually really worried about exact values you should use something like Decimal.js which handles numbers as strings rather than floats. Especially if you're doing anything like calculating money.

Mike
  • 1,471
  • 12
  • 17
  • The `.toFixed()` function returns a string, not a number. – Pointy Aug 28 '19 at 02:39
  • True, but it still rounds up correctly, so if you need it as a number then just cast it. Besides, he's just printing out the results anyway so he needs it as a string – Mike Aug 28 '19 at 02:41
  • yes but as soon as you cast it you're back in binary floating-point land, and it's not necessarily the exact value for all the same reasons. – Pointy Aug 28 '19 at 02:42
  • That's fair, but at that point you really shouldn't be using floating points anyway, and should use something like Decimal.js – Mike Aug 28 '19 at 02:43
  • 1
    based on requirements, the person needs to print it, so the conversion to String shouldn't be an issue in this case, but indeed would be helpful as an update to your answer, something like: **Observation**: (...) – azbarcea Aug 28 '19 at 02:44
0

Its not with javascript. Any language which uses IEEE Standard 754 to represennt real numbers has this. Basically, most of the real numbers cannot be represented accurately. So what happens an approximation (usually 53 bits) is taken.

Here you can use Number.toPrecision().

himank
  • 439
  • 3
  • 5
0

Based on your work:

function round( number, step ) {
    const inverseStep = 1 / step;
    return Math.round( number * inverseStep ) / inverseStep;
}

for ( let i = 11; i <= 30; i++ ) {
    const rounded = round( i * 0.015, 0.001 );
    console.log( `${i} * 0.015 = ${rounded}` );
}

your output will be:

'11 * 0.015 = 0.165'
'12 * 0.015 = 0.18'
'13 * 0.015 = 0.195'
'14 * 0.015 = 0.21'
'15 * 0.015 = 0.225'
'16 * 0.015 = 0.24'
'17 * 0.015 = 0.255'
'18 * 0.015 = 0.27'
'19 * 0.015 = 0.285'
'20 * 0.015 = 0.3'
'21 * 0.015 = 0.315'
'22 * 0.015 = 0.33'
'23 * 0.015 = 0.345'
'24 * 0.015 = 0.36'
'25 * 0.015 = 0.375'
'26 * 0.015 = 0.39'
'27 * 0.015 = 0.405'
'28 * 0.015 = 0.42'
'29 * 0.015 = 0.435'
'30 * 0.015 = 0.45'
azbarcea
  • 3,323
  • 1
  • 20
  • 25
  • 1
    From the duplicate: `parseFloat((i * 0.015).toFixed(10))` does the job without the *step* parameter. Both approaches have limits. – RobG Aug 28 '19 at 08:46
  • Correct. I was trying to limit to his example and code with minimal changes. @RobG I upvoted your comment :-) – azbarcea Aug 28 '19 at 17:20
0

The guys did an amazing job why this is happening In case you still need to convert it to a number you can use parseFloat with the Number in which it explicitly converts the supplied value into a number for processing

for ( let i = 11; i <= 30; i++ ) {
    console.log( `${i} * 0.015 = ${parseFloat(Number(i * 0.015).toFixed(3))}` );
}

Here is a nice explanation of why it's happening and different approaches to tackle it.

Reference and explanation

Seder
  • 2,735
  • 2
  • 22
  • 43
0

Using the correction factor trick.

$ cat num

let cf = 10
for ( let i = 11; i <= 30; i++ ) {
    let num = (i * cf) * (.015 * cf) / (cf *cf);
    console.log( `${i} * 0.015 = ${num}` );
}
$ node ./num
11 * 0.015 = 0.165
12 * 0.015 = 0.18
13 * 0.015 = 0.195
14 * 0.015 = 0.21
15 * 0.015 = 0.225
16 * 0.015 = 0.24
17 * 0.015 = 0.255
18 * 0.015 = 0.27
19 * 0.015 = 0.285
20 * 0.015 = 0.3
21 * 0.015 = 0.315
22 * 0.015 = 0.33
23 * 0.015 = 0.345
24 * 0.015 = 0.36
25 * 0.015 = 0.375
26 * 0.015 = 0.39
27 * 0.015 = 0.405
28 * 0.015 = 0.42
29 * 0.015 = 0.435
30 * 0.015 = 0.45

Use the toFixed option will get you close if you don't mind the zero padding.

$ cat num

for ( let i = 11; i <= 30; i++ ) {
    let num = i * .015;
    num = num.toFixed(3);
    console.log( `${i} * 0.015 = ${num}` );
}
$ node ./num
11 * 0.015 = 0.165
12 * 0.015 = 0.180
13 * 0.015 = 0.195
14 * 0.015 = 0.210
15 * 0.015 = 0.225
16 * 0.015 = 0.240
17 * 0.015 = 0.255
18 * 0.015 = 0.270
19 * 0.015 = 0.285
20 * 0.015 = 0.300
21 * 0.015 = 0.315
22 * 0.015 = 0.330
23 * 0.015 = 0.345
24 * 0.015 = 0.360
25 * 0.015 = 0.375
26 * 0.015 = 0.390
27 * 0.015 = 0.405
28 * 0.015 = 0.420
29 * 0.015 = 0.435
30 * 0.015 = 0.450
WaltDe
  • 1,715
  • 8
  • 17