Может ли 12.1 быть представлено точно как число с плавающей запятой?

StackOverflow https://stackoverflow.com/questions/1904321

Вопрос

Это относится к комментариям в этот вопрос:

Этот код на 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 все равно будет в порядке)

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top