«Интервью воодушевленное» — равно 0,1 + 0,2 0,3 ит | бычье новогоднее эссе

JavaScript опрос

предисловие

Когда вы идете на собеседование в компанию, работающую в сфере интернет-финансов или электронной коммерции, вы обычно сталкиваетесь с чем-то вроде «0.1+0.20.1+0.2равный0.30.3? «Этот вопрос — хорошее предложение для фронтенд-людей, не имеющих академического образования. Некоторые люди знают, что0.1+0.20.1+0.2не равно0.30.3, но если вы продолжите спрашивать почему, вы не сможете внятно ответить.

В этой колонке собраны ответы0.1+0.20.1+0.2не равно0.30.3идея, прежде чем ответить0.1+0.20.1+0.2процесс расчета.

Процесс расчета 0,1+0,2Процесс расчета

1. Преобразование десятичного числа в двоичное

Все вычисления внутри JS выполняются в бинарном виде.так операция0.1+0.20.1+0.2первый0.10.1а также0.20.2Преобразование из десятичной в двоичную.

  • Алгоритм преобразования 0.1 в двоичный код:

    0,1*2=0,2====== Вынуть целую часть 0

    0,2*2=0,4====== Вынуть целую часть 0

    0,4*2=0,8====== Вынуть целую часть 0

    0,8*2=1,6====== Вынуть целую часть 1

    0,6*2=1,2====== Вынуть целую часть 1

    Это будет бесконечно повторяться

    0,2*2=0,4====== Вынуть целую часть 0

    0,4*2=0,8====== Вынуть целую часть 0

    0,8*2=1,6====== Вынуть целую часть 1

    0,6*2=1,2====== Вынуть целую часть 1

    Таким образом, преобразование 0,1 в двоичное число: 0,0001 1001 1001 1001...

  • Алгоритм конвертации 0.2 в бинарник:

    0,2*2=0,4====== Вынуть целую часть 0

    0,4*2=0,8====== Вынуть целую часть 0

    0,8*2=1,6====== Вынуть целую часть 1

    0,6*2=1,2====== Вынуть целую часть 1

    Это будет бесконечно повторяться

    0,2*2=0,4====== Вынуть целую часть 0

    0,4*2=0,8====== Вынуть целую часть 0

    0,8*2=1,6====== Вынуть целую часть 1

    0,6*2=1,2====== Вынуть целую часть 1

    Таким образом, 0,2, преобразованное в двоичное число, равно: 0,0011 0011 0011 0011...

Обратите внимание здесь0.10.1а также0.20.2Преобразованный двоичный файл бесконечен. Кроме тогоВ современных браузерах для хранения бинарника используется бинарник в виде чисел с плавающей запятой, поэтому необходимо преобразовать бинарник, преобразованный выше, в бинарник в виде чисел с плавающей запятой.

2. Преобразование в число с плавающей запятой

Числа с плавающей запятой делятся на числа одинарной точности, соответствующие 32-разрядным операционным системам, и числа двойной точности, соответствующие 64-разрядным операционным системам. Большинство современных операционных систем являются 64-разрядными операционными системами, поэтому здесь я только объясню, как преобразовать двоичный файл в двоичный файл с плавающей запятой двойной точности.

В числах с плавающей запятой двойной точности используется 1 бит для представления бита знака, 11 бит для представления бита экспоненты и 52 бита для представления десятичного бита, как показано на следующем рисунке:

  • Бит знака: 0 для положительных чисел, 1 для отрицательных чисел;

  • Биты экспоненты:阶数+偏移量Заказ:2e112^{e-1}-1,ee- количество цифр кода заказа. Смещение заключается в перемещении десятичной точки только в целые числа11Количество цифр для перемещения, когда время, положительное число означает перемещение влево, отрицательное число означает перемещение вправо;

  • Десятичный разряд: число после двоичной точки.

Следующий поставить0.10.1преобразован в двоичный0.0001100110011001......0.0001 1001 1001 1001......Преобразование в двоичный формат с плавающей запятой.

  • Сначала переместите десятичную точку только в целое число11, чтобы переместиться на 4 бита вправо, поэтому смещение равно4-4, по формуле вычисления числа показателей211114=10192^{11-1}-1-4 = 1019,Пучок10191019преобразовать в двоичный11111110111111111011, если не хватает 11 бит, его нужно заполнить нулями, и, наконец, количество степеней0111111101101111111011;

  • десятичные разряды100110011001......1001 1001 1001......, так как можно зарезервировать только 52 десятичных знака, 53-я цифра равна 1, поэтому 1 увеличивается.

Результат преобразования показан на следующем рисунке:

Аналогично, положить0.20.2преобразован в двоичный0.00110011 00110011......0.0011 0011 0011 0011......Преобразованный в двоичный формат в виде чисел с плавающей запятой, результат преобразования показан на следующем рисунке:

Сложение с плавающей запятой

При добавлении чисел с плавающей запятой необходимо сравнить, соответствует ли количество показателей степени.Если они совпадают, десятичные разряды добавляются напрямую.Если они несовместимы, число показателей степени должно число показателей должно быть скорректировано от меньшего к большему.

Для удобства записи число с плавающей запятой, преобразованное из 0,1, называется 0,1, а число с плавающей запятой, преобразованное из 0,2, называется 0,2.

Бит экспоненты 0,1 равен10191019, бит экспоненты 0,2 равен10201020. Следовательно, чтобы добавить 1 к биту экспоненты 0,1, то есть сдвинуть десятичную точку 0,1 влево на 1 бит и зафиксировать целочисленный бит числа с плавающей запятой на 1. Процесс выглядит следующим образом

1.1001100110011001100110011001100110011001100110011010   原先
0.11001100110011001100110011001100110011001100110011010  移动后  
0.1100110011001100110011001100110011001100110011001101   将小数的第53位舍去,因为为0故不需进1

в результате чего десятичные разряды 0,1 становятся следующими:

Теперь, когда показатели степени 0,1 и 0,2 одинаковы, добавьте десятичные дроби напрямую.

    1100110011001100110011001100110011001100110011001101 0.1的小数位
+   1001100110011001100110011001100110011001100110011010 0.2的小数位
=  10110011001100110011001100110011001100110011001100111

Вы обнаружите, что теперь есть еще один десятичный знак, превышающий 52 цифры, поэтому последняя цифра десятичного разряда должна быть усечена, а последняя цифра десятичного разряда равна 1, поэтому 1 следует добавить, как показано ниже:

10110011001100110011001100110011001100110011001100111
1011001100110011001100110011001100110011001100110100

Усечение последней цифры десятичного разряда эквивалентно смещению запятой на один разряд влево, поэтому показатель степени должен быть увеличен на 1, а показатель степени в это время равен показателю 0,2.10211021, после добавления 1 становится10211021, преобразованный в двоичный как0111111110101111111101, Тогда число с плавающей запятой после сложения будет следующим:

Преобразование числа с плавающей запятой в десятичное

После того, как вычисление двоичного числа с плавающей запятой завершено, результат (двоичное число с плавающей запятой) преобразуется в десятичное, и формула преобразования(1)s*2e1023*(1+i=152(Mi*2i)){(-1)}^s*2^{e-1023}*(1+\sum_{i=1}^{52}(M_i*2^{-i})), s — бит знака равен 0 или 1, e — значение бита экспоненты с плавающей запятой, преобразованное в десятичное число, i — количество десятичных разрядов слева направо, первая цифраi=1i=1,MiM_iУказывает, что каждый бит имеет значение 0 или 1.

Затем преобразуйте двоичное число с плавающей запятой в десятичное по формуле:

(1)0*22*(1+1*21+0+1*23+1*24+...)({-1})^0*2^{-2} * (1+ 1*2^-1 + 0 + 1*2^-3 + 1*2^-4 + ...)

Результат выглядит следующим образом:

0.30000000000000004440892098500626161694526672363281250.3000000000000000444089209850062616169452667236328125

Из-за проблем с точностью только0.300000000000000040.30000000000000004.

Отвечать

0.1+0.20.1+0.2не равно0.30.3,Потому что0.1+0.20.1+0.2При вычислении произошло две потери точности. первый раз в0.10.1а также0.20.2При преобразовании в двоичное число с плавающей запятой двойной точности, поскольку десятичный разряд двоичного числа с плавающей запятой может хранить только 52 бита, 53-я цифра после запятой должна быть 1, затем 1 и 0 отбрасываются, что приводит к потеря точности... второй раз в0.10.1а также0.20.2После преобразования в двоичные числа с плавающей запятой в процессе сложения двоичных чисел с плавающей запятой добавление десятичных разрядов приводит к еще одному десятичному разряду, причем 53-я цифра должна быть 1, затем добавляется 1 и отбрасывается 0. , вызывая еще одну потерю точности. в конечном итоге привести к0.1+0.20.1+0.2не равно0.30.3.

расширять

Если вы ответите, интервьюер может продолжить спрашивать вас: "0.1+0.20.1+0.2не равно0.30.3Это вызовет эти ошибки? "

Вы можете ответить так: «Ошибка, из-за которой страница статистики будет отображаться беспорядочно, а300.01300.01скидка300300После юаней сумма платежа недостаточна0.010.01Юань и другие подобные баги. "

Вы также можете продолжать спрашивать: «Как решить0.1+0.20.1+0.2не равно0.30.3Эта проблема".

Ответ: "Это можно решить с помощью математической библиотеки Math.js или с помощьюtoFixed()округлить результат расчета, ноtoFixed()Существуют также ошибки точности округления в Chrome или Firefox. Можно использоватьMath.roundЧтобы решить ошибку точности, например, положить2.552.55округление зарезервировано11десятичные разряды, поставить первым2.55*102.55*10получать25.525.5%, затем используйтеMath.roundОкругление25.525.5%, получите2525, затем поставьте25÷1025÷10получать2.52.5, что косвенно обеспечивает округление. Можно использоватьMath.powсделать простой пакетMath.round(Math.pow(10, m) * number) / Math.pow(10, m)numberэто число, которое нужно округлить,mзаключается в том, чтобы сохранить несколько знаков после запятой.