Почему мои объекты BigDecimal инициализируются неожиданными ошибками округления?
В Ruby 2.2.0 почему:
BigDecimal.new(34.13985572755337, 9)
равно 34.0, но
BigDecimal.new(34.13985572755338, 9)
равно 34.1398557?
Обратите внимание, что я запускаю это на 64-битной машине.
Ответ 1
Инициализировать с помощью строк вместо плавающих
В общем, вы не можете получить надежное поведение с помощью Floats. Вы делаете ошибку, инициализируя свои BigDecimals значениями Float вместо значений String, что в начале вводит некоторую неточность. Например, на моей 64-битной системе:
float1 = 34.13985572755337
float2 = 34.13985572755338
# You can use string literals here, too, if your Float can't be properly
# represented. For example:
#
# BigDecimal.new("34.13985572755337", 9)
#
# would be safer, but Float#to_s works fine with the provided corpus.
bd1 = BigDecimal.new(float1.to_s, 9)
bd2 = BigDecimal.new(float2.to_s, 9)
bd1.to_s
#=> "0.3413985572755337E2"
bd2.to_s
#=> "0.3413985572755338E2"
bd1.to_f == float1
#=> true
bd2.to_f == float2
#=> true
Это один из тех случаев, когда внутреннее представление аргументов имеет значение. Поэтому ваш пробег будет зависеть от того, как вы инициализируете свой объект.