Проверка работоспособности Ruby BigDecimal (плавающая точка newb)

Мое понимание, корректное это с Ruby BigDecimal типы (даже с переменной точностью и длинами шкалы) должны вычислить точно, или я должен ожидать интриги с плавающей точкой?

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

20
задан Arslan Ali 25 March 2015 в 08:32
поделиться

1 ответ

Есть две распространенные ошибки при работе с арифметикой с плавающей запятой.

Первая проблема заключается в том, что с плавающей запятой Ruby фиксированная точность. На практике это будет либо 1) без проблем, либо 2) катастрофой, либо 3) чем-то средним. Рассмотрим следующее:

# float
1.0e+25 - 9999999999999999900000000.0
#=> 0.0

# bigdecimal
BigDecimal("1.0e+25") - BigDecimal("9999999999999999900000000.0")
#=> 100000000

Разница в точности в 100 миллионов! Довольно серьезно, не так ли?

За исключением того, что ошибка точности составляет всего около 0,000000000000001% от исходного числа. Действительно ли это проблема или нет - решать вам. Но проблема устраняется использованием BigDecimal , поскольку он имеет произвольную точность. Ваш единственный предел - это память, доступная для Ruby.

Вторая проблема заключается в том, что числа с плавающей запятой не могут точно выразить все дроби. В частности, у них есть проблемы с десятичными дробями, потому что числа с плавающей запятой в Ruby (и большинстве других языков) являются двоичными числами с плавающей запятой. Например, десятичная дробь 0,2 - это вечно повторяющаяся двоичная дробь ( 0,001100110011 ... ). Это никогда не может быть точно сохранено в двоичном формате с плавающей запятой, независимо от точности.

Это может иметь большое значение при округлении чисел. Рассмотрим:

# float
(0.29 * 50).round
#=> 14  # not correct

# bigdecimal
(BigDecimal("0.29") * 50).round
#=> 15  # correct

BigDecimal может точно описывать десятичные дроби. Однако есть дроби, которые нельзя точно описать десятичной дробью. Например, 1/9 - это вечно повторяющаяся десятичная дробь ( 0,1111111111111 ... ).

Опять же, это вас укусит, если округлить число. Учтите:

# bigdecimal
(BigDecimal("1") / 9 * 9 / 2).round
#=> 0  # not correct

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

Некоторые выводы:

  • Десятичные числа с плавающей запятой хороши, если вы делаете вычисления с десятичными дробями (например, с деньгами).
  • BigDecimal Ruby также хорошо работает, если вам нужны числа с плавающей запятой произвольной точности, и вам наплевать, являются ли они десятичными или двоичными с плавающей запятой.
  • Если вы работаете с (научными) данными, вы обычно имеете дело с числами фиксированной точности; Встроенных в Ruby поплавков, вероятно, будет достаточно.
  • Никогда нельзя ожидать, что арифметика с любым видом с плавающей запятой будет точной во всех ситуациях.
33
ответ дан 30 November 2019 в 00:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: