Почему Ruby добавляет точность / цифры при расчете мода?

Мне нужно сделать некоторые вычисления с координатами и натолкнулся на это (по крайней мере для меня) странное поведение. Может кто-нибудь объяснить, почему это происходит?

$ long
=> 49.0126760222489
$ long % long.floor
=> 0.012676022248896857

Я ожидал, что последняя строка будет содержать только цифры после периода, от long, но вместо этого есть дополнительный 6857 прикрепленный к концу.

Почему он добавляет цифры?

Откуда поступает информация?

2 ответа

Решение

Я предполагаю, что мы просто видим впечатления поплавка. См. То есть 0.0126760222489 % 1.0 а также 1.0126760222489 % 1.0, Вы могли бы подумать, что результат должен быть таким же, но нет - IEEE754 с плавающей запятой / двойным не гарантирует идеальных результатов, и по умолчанию они используются в Ruby для хранения значений с плавающей запятой.

Это даже несколько показано в документах

6543.21.modulo(137)      #=> 104.21
6543.21.modulo(137.24)   #=> 92.9299999999996

Вы можете видеть, что второй результат имеет небольшую ошибку. На самом деле, на Ruby 2.3.1 я запустил первую строку и получил:

pry(main)> 6543.21.modulo(137)
=> 104.21000000000004

Это не обязательно связано с модулем, и не всегда видно:

[30] pry(main)> 10.0126760222489 - 0.0
=> 10.0126760222489
[31] pry(main)> 10.0126760222489 - 1.0
=> 9.0126760222489
[32] pry(main)> 10.0126760222489 - 2.0
=> 8.0126760222489
[33] pry(main)> 10.0126760222489 - 3.0
=> 7.0126760222489
[34] pry(main)> 10.0126760222489 - 4.0
=> 6.0126760222489
[35] pry(main)> 10.0126760222489 - 5.0
=> 5.0126760222489
[36] pry(main)> 10.0126760222489 - 6.0
=> 4.0126760222489
[37] pry(main)> 10.0126760222489 - 7.0
=> 3.0126760222489004

Каждое программное обеспечение, которое использует стандартные операции с плавающей запятой, должно учитывать эти небольшие ошибки. Если вы не можете справиться с этим по какой-то причине, то вы можете использовать bigdecimal (должен быть уже включен в ваш Ruby), с фиксированной запятой или какой-либо подобной числовой библиотекой

require 'bigdecimal'

BigDecimal('6543.21').modulo(137).to_s
=> "0.10421E3"

BigDecimal('6543.21').modulo(137).to_f
=> 104.21

Имейте в виду, что "bigdecimal" может быть медленнее и использовать больше памяти.

Кажется, это не имеет ничего общего с самим Ruby, но:

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

Документация

Из вышеприведенных документов также можно получить несколько вариантов обхода поведения с плавающей точкой. Приведенная выше документация может рассматриваться как официальная, поскольку она предоставлена ruby-doc.org.

Другие вопросы по тегам