Why does parseFloat() in JavaScript produce consis

2019-07-14 18:45发布

While providing an answer to a question about adding a method that increments a float value in an html input, I came across what I know is a common issue with IEEE Floating point math. This is an example (with a demo) of what I am seeing:

<input type="text" name="BoqTextBox" id="BoqTextBox" value="0.001" />
<input type="Button" id='AddButton' value="+" />
<script>
  $('#AddButton').on('click', function () {
    var input = $('#BoqTextBox');
    input.val(parseFloat(input.val(), 10) + 1);
  })
</script>

Clicking on the + button, causes the input to increment at first like this (using this converter for IEEE Binary Representations):

                     S|   Exp   |      Mantissa
0.001              //0 0111 0101 000 0011 0001 0010 0110 1111
1.001              //0 0111 1111 000 0000 0010 0000 1100 0101
2.001              //0 1000 0000 000 0000 0001 0000 0110 0010
3.001              //0 1000 0000 100 0000 0001 0000 0110 0010
4.0009999999999994 //0 1000 0001 000 0000 0000 1000 0011 0001
5.0009999999999994 //0 1000 0001 010 0000 0000 1000 0011 0001
6.0009999999999994 //0 1000 0001 100 0000 0000 1000 0011 0001
7.0009999999999994 //0 1000 0001 110 0000 0000 1000 0011 0001
8.001              //0 1000 0010 000 0000 0000 0100 0001 1001
9.001              //0 1000 0010 001 0000 0000 0100 0001 1001
10.001             //0 1000 0010 010 0000 0000 0100 0001 1001
... then predictably like this until ...
15.001             //0 1000 0010 111 0000 0000 0100 0001 1001
16.000999999999998 //0 1000 0011 000 0000 0000 0010 0000 1100
17.000999999999998 //0 1000 0011 000 1000 0000 0010 0000 1100
18.000999999999998 //0 1000 0011 001 0000 0000 0010 0000 1100
19.000999999999998 //0 1000 0011 001 1000 0000 0010 0000 1100
... then predictably like this until ...
31.000999999999998 //0 1000 0011 111 1000 0000 0010 00001100
32.001             //0 1000 0100 000 0000 0000 0001 00000110
33.001             //0 1000 0100 000 0100 0000 0001 00000110
... then predictably like that up to 256 (a power of 8 seemed fitting to test) ...

What I don't yet know is why does this behavior manifest itself at those specific values, and what logical steps can I take to mitigate its effects? Rounding for display purposes while keeping the actual calculated value stored someplace else?

1条回答
我只想做你的唯一
2楼-- · 2019-07-14 19:06

As you guessed you should print outputs only after rounding them. That behaviour is the result of difference between decimal & binary base numbers, because there are decimal numbers which don't have exact representation in binary format of floating point numbers, and therefore some accuracy is lost when storing it into binary and then reading it again. Floating point numbers are represented in CPU in the form 1.XXXXX...XXXX * 2^P, so from this form you can see that not all decimal numbers have such representation with limited length of XXXXXX...XXX.

查看更多
登录 后发表回答