Может ли 12.1 быть представлено точно как число с плавающей запятой?
-
19-09-2019 - |
Вопрос
Это относится к комментариям в этот вопрос:
Этот код на Java выдает 12.100000000000001, и при этом используются 64-разрядные дубли, которые могут представлять 12.1 в точности.– Пиролистический
Это правда?Я почувствовал, что, поскольку число с плавающей запятой представлено в виде суммы степеней двойки, вы не можете точно представить 12.1, независимо от того, сколько у вас битов.Однако, когда я реализовал оба алгоритма и напечатал результаты их вызова с помощью (12.1, 3) со многими значащими цифрами, я получаю для его и моего соответственно:
12.10000000000000000000000000000000000000000000000000000000000000000000000000 12.10000000000000100000000000000000000000000000000000000000000000000000000000
Я напечатал это с помощью String.format("%76f")
.Я знаю, что нулей больше, чем необходимо, но я не вижу никакого округления в 12.1 .
Решение
Нет.Как отмечали другие в продолжениях к его комментарию, никакая сумма степеней двойки (конечного числа) никогда не может составить ровно 12,1.Точно так же, как вы не можете точно представить 1/3 в десятичной базе, независимо от того, сколько цифр вы используете после запятой.
Другие советы
В двоичном формате 12.1 равно:
1100.000110011001100110011...
Поскольку это не завершается, оно не может быть представлено точно в 53 значащих битах double или любого другого двоичного типа с плавающей запятой конечной ширины.
Попробуйте выразить 0.1 в двоичном формате:
0,5 - это слишком много
0,25 - это слишком много
0,125 - это слишком много
Подходит 0,0625, и остается остаток 0,0375
Подходит 0,03125, и остается остаток 0,00625
0,015625 - это слишком много
0.0078125 слишком большой
Подходит 0,00390625, и остается остаток в размере 0,00234375
Подходит 0,001953125, и остается остаток в размере 0,000390625
Это будет повторяться бесконечно, создавая базовое значение 2, равное 0.00011001100...
Нет, это не может быть выражено точно в двойнике.Если Java поддерживает BCD, или десятичную дробь с фиксированной запятой, это будет работать точно.
Не в двоичном формате, нет.Если вы позволите мне проявить фантазию, вы могли бы использовать "двоично-десятичный код с плавающей запятой" (который, насколько мне известно, никогда не был реализован).:
12.1 = 0000 . 0001 0010 0001 * (10^2)
В двоичном коде все ненулевые значения имеют вид 1.xyz * m
, и форма IEEE использует это преимущество, чтобы опустить начальную 1.Я не уверен, что эквивалентно FP-BCD, поэтому я выбрал значения вида 0.xyz * m
вместо этого.
Я предлагаю прочитать Что каждый специалист по информатике должен знать Об арифметике с плавающей запятой.Тогда ты будешь знать наверняка.:)
Способ увидеть, что такое double довольно точно, - это преобразовать его в BigDecimal .
// prints 12.0999999999999996447286321199499070644378662109375
System.out.println(new BigDecimal(12.1));
Да, вы можете точно представить 12.1 с плавающей запятой.Вам просто нужно десятичное представление с плавающей запятой, а не двоичное.
Используйте тип BigDecimal, и вы будете представлять его в точности!
Нет, десятичное число 12.1
не может быть представлено в виде конечного (завершающего) двоичного числа с плавающей запятой.
Помни это 12.1
является рациональным числом 121/10
.Обратите внимание, что эта дробь имеет наименьший размер (не может быть уменьшена путем удаления общих дробей в числителе и знаменателе).
Предположим (чтобы прийти к противоречию), что 121/10
может быть записан также как n / (2**k)
где n
и k
являются некоторыми целыми положительными числами, и 2**k
обозначает k
степень двойки.У нас был бы контрпример к уникальная факторизация.В частности
10 * n == 2**k * 121
где левая сторона делится на 5, а правая - нет.
Один из вариантов, который вы можете использовать, - это не сохранять v = 0.1, а вместо этого сохранять v10 = 1.Просто разделите на 10, когда это необходимо ( деление создаст ошибку усечения в вашем результате, но v все равно будет в порядке)
В этом случае вы в основном выполняете взлом с фиксированной точкой, но сохраняете число с плавающей запятой.Но обычно не стоит этого делать, если вам действительно не нужно.