Почему parseFloat() в JavaScript дает согласованные, но унифицированные результаты?

Предоставляя ответ на вопрос о добавлении метода, который увеличивает значение с плавающей запятой в html input, я наткнулся на то, что я know — это общая проблема с математикой с плавающей запятой IEEE. Это пример (с демо) того, что я вижу:

<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>

Нажатие на кнопку + приводит к тому, что ввод сначала увеличивается следующим образом (с использованием этого converter для двоичных представлений IEEE):

                     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) ...

Чего я пока не знаю, так это почему такое поведение проявляется при этих конкретных значениях и какие логические шаги я могу предпринять, чтобы смягчить его последствия? Округление для целей отображения при сохранении фактического расчетного значения в другом месте?


person Jason Sperske    schedule 22.03.2013    source источник
comment
я бы округлил на дисплее   -  person John Dvorak    schedule 23.03.2013
comment
@JanDvorak Я уверен, что другого решения нет ...   -  person Juan Mendes    schedule 23.03.2013
comment
@ХуанМендес BigDecimal   -  person John Dvorak    schedule 23.03.2013
comment
@MattBall, вот и мои выходные :)   -  person Jason Sperske    schedule 23.03.2013
comment
@JasonSperske Вопрос касается точности JavaScript/с плавающей запятой, а не использования другого типа/языка в целом.   -  person Juan Mendes    schedule 23.03.2013
comment
@JuanMendes, я в замешательстве. Я не упомянул какой-либо другой язык, кроме JavaScript (это ссылка на конвертер IEEE?) Или это было адресовано Яну Двораку?   -  person Jason Sperske    schedule 23.03.2013
comment
Может быть, нам стоит добавить еще одну классическую ошибку в список Виццини: никогда не доверять значениям с плавающей запятой. с более чем 4 десятичными знаками, когда задействован IEEE 754 :)   -  person Jason Sperske    schedule 23.03.2013
comment
@JasonSperske Извините, это должно было быть в JanDvorak   -  person Juan Mendes    schedule 23.03.2013


Ответы (1)


Как вы уже догадались, выводить результаты следует только после их округления. Такое поведение является результатом разницы между десятичными и двоичными базовыми числами, поскольку существуют десятичные числа, которые не имеют точного представления в двоичном формате чисел с плавающей запятой, и поэтому некоторая точность теряется при сохранении их в двоичном формате и последующем чтении. . Числа с плавающей запятой представлены в ЦП в виде 1.XXXXX...XXXX * 2^P, поэтому из этой формы видно, что не все десятичные числа имеют такое представление с ограниченной длиной XXXXXX...XXX.

person artahian    schedule 22.03.2013
comment
Я полностью на той же странице, что и вы, в этом, может быть, то, что я искал, было немного более глубоким, посмотрите, почему числа отрываются в этот момент, а затем воссоединяются (может быть, мне нужно посмотреть, что такое мантисса) - person Jason Sperske; 23.03.2013
comment
Мантисса — это та часть, которую я отметил 1.XXXXXX...XXXX. Вы можете прочитать больше на kipirvine.com/asm/workbook/floating_tut.htm. - person artahian; 23.03.2013