2016-10-14 2 views
2

Некоторые числа с плавающей точкой имеют присущую неточность из двоичного представления чисел с плавающей точкой:Почему рубины BigDecimal показывают неточность представления, похожую на float?

> puts "%.50f" % (0.5) # cleanly representable 
0.50000000000000000000000000000000000000000000000000 

> puts "%.50f" % (0.1) # not cleanly representable 
0.10000000000000000555111512312578270211815834045410 

Это nothingnew. Но почему у рубина BigDecimal также показано это поведение?

> puts "%.50f" % ("0.1".to_d) 
0.10000000000000000555111512312578270211815834045410 

(я использую рельсы обсчитывать .to_d вместо BigDecimal.new для краткости только, это не рельсы конкретный вопрос.)

Вопрос: Почему "0.1".to_d еще показываются ошибки на порядок 10 -17? Я думал, цель BigDecimal была явно, чтобы избежать таких неточностей?

Сначала я думал, что это было, потому что я был преобразование уже неточным плавающего 0.1 к BigDecimal и BigDecimal только без потерь, представляющий inaccuraccy. Но я убедился, что использую конструктор строк (как в приведенном выше фрагменте), что должно избежать проблемы.


EDIT:

Немного больше исследований показывают, что BigDecimal действительно все еще внутри представляют собой вещи чисто. (. Очевидный, поскольку в противном случае это будет огромной ошибки в широко используемой системе) Вот пример с операцией, которая будет по-прежнему показывать сообщение об ошибке:

> puts "%.50f" % ("0.1".to_d * "10".to_d) 
1.00000000000000000000000000000000000000000000000000 

Если представление было с потерей данных, что бы показать та же ошибка, что и выше, просто сдвинута на порядок. Что здесь происходит?

ответ

5

Спецификатор %.50f принимает значение с плавающей запятой, так что десятичное значение должно быть преобразовано в плавающую точку, прежде чем оно будет отображаться для отображения, и, как таковое, подвергается одинаковому шуму с плавающей точкой, вы получаете обычные значения с плавающей запятой.

sprintf и друзья, как и метод String#%, делают преобразования автоматически в зависимости от типа, указанного в заполнителе.

Чтобы подавить то, что вам нужно будет использовать .to_s method на BigDecimal number напрямую. Может потребоваться опциональный спецификатор формата, если вы хотите определенное количество мест, и это может быть привязано к заполнительу %s в другой строке.

Смежные вопросы