Может ли 12.1 быть представлен точно как число с плавающей запятой? - PullRequest
4 голосов
/ 15 декабря 2009

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

Этот код в Java производит 12.100000000000001, и он использует 64-битные двойные числа, которые могут точно представлять 12.1. - пиролистический

Это правда? Я чувствовал, что, поскольку число с плавающей запятой представлено как сумма степеней двух, вы не можете точно представить 12,1, независимо от того, сколько у вас битов. Однако, когда я реализовал оба алгоритма и напечатал результаты вызова их с помощью (12.1, 3) со многими значащими цифрами, я получил для его и моего соответственно:

12.10000000000000000000000000000000000000000000000000000000000000000000000000 12.10000000000000100000000000000000000000000000000000000000000000000000000000

Я напечатал это, используя String.format("%76f"). Я знаю, что это больше нулей, чем необходимо, но я не вижу округления в 12.1.

Ответы [ 9 ]

15 голосов
/ 15 декабря 2009

Нет. Как отмечали другие в последующих комментариях к нему, никакая сумма (конечное число) степеней двух никогда не может составить ровно 12,1. Точно так же, как вы не можете представить 1/3 точно в базовой десятке, независимо от того, сколько цифр вы используете после десятичной точки.

12 голосов
/ 15 декабря 2009

В двоичном коде 12,1:

1100.000110011001100110011...

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

9 голосов
/ 15 декабря 2009

Попробуйте выразить 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 или десятичную с фиксированной запятой, это будет работать точно.

3 голосов
/ 15 декабря 2009

Не в двоичном виде, нет. Если вы позволите мне проявить фантазию, вы могли бы использовать «десятичное число с двоичным кодом с плавающей запятой» (которое, насколько я знаю, никогда не было реализовано):

12.1 = 0000 . 0001 0010 0001 * (10^2)

В двоичном формате все ненулевые значения имеют форму 1.xyz * m, и форма IEEE использует это, чтобы опустить ведущий 1. Я не уверен, каков эквивалент для FP-BCD, поэтому я пошел вместо значений вида 0.xyz * m.

2 голосов
/ 05 ноября 2014

Нет, десятичное число 12.1 не может быть представлено как конечное (завершающее) двоичное число с плавающей точкой.

Помните, что 12.1 - это рациональное число 121/10. Обратите внимание, что эта доля находится в самых низких терминах (не может быть уменьшена путем удаления общих чисел числителя и знаменателя).

Предположим (чтобы достичь противоречия), что 121/10 можно записать также как n / (2**k), где n и k - некоторые положительные целые числа, а 2**k обозначает k -ую степень двух , У нас был бы контрпример к уникальной факторизации . В частности

10 * n == 2**k * 121

где левая сторона делится на 5, а правая - нет.

2 голосов
/ 28 января 2014

Да, вы можете точно представлять 12,1 в плавающей запятой. Вам просто нужно десятичное представление с плавающей точкой, а не двоичное.

Используйте тип BigDecimal, и вы точно его представите!

2 голосов
/ 15 декабря 2009

Чтобы узнать, что такое двойной тип, достаточно просто преобразовать его в BigDecimal.

// prints 12.0999999999999996447286321199499070644378662109375
System.out.println(new BigDecimal(12.1));
2 голосов
/ 15 декабря 2009

Предлагаю прочитать Что должен знать каждый учёный об арифметике с плавающей запятой . Тогда ты точно будешь знать. :)

0 голосов
/ 15 декабря 2009

Один из вариантов, который вы можете использовать, - не хранить v = 0.1, а хранить v10 = 1. Просто разделите на 10 при необходимости (разделение приведет к ошибке усечения в вашем результате, но v все равно будет в порядке)

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

...