Float
Float в Ruby — это класс, представляющий вещественные числа (дробные числа с плавающей точкой двойной точности). Они записываются с десятичной точкой (например, 3.14) и используются для вычислений, требующих точности.
Float могут иметь проблемы с точностью при десятичных вычислениях из-за двоичного представления. Ruby всегда преобразует числа с плавающей точкой из десятичного формата в двоичный и наоборот. При преобразовании десятичного числа в двоичное, результирующее двоичное число может быть бесконечным. Математически это всё нормально. Но на практике у компьютера нет бесконечной памяти, поэтому Ruby должен в какой-то момент обрезать бесконечное число, иначе оно заполнит всю память компьютера и станет бесполезным. Этот механизм округления чисел и приводит к ошибке округления.
>> 0.1 + 0.05 == 0.15
=> false
>> 0.1 + 0.05
=> 0.15000000000000002
>> sprintf("%0.50f", 0.10 + 0.05)
=> "0.15000000000000002220446049250313080847263336181641"
>> sprintf("%0.50f", 0.10)
=> "0.10000000000000000555111512312578270211815834045410"
>> sprintf("%0.50f", 0.05)
=> "0.05000000000000000277555756156289135105907917022705"
Как видно из примера выше, Ruby обрезает значение Float при преобразовании полученной бесконечности при постановке и извлечения из памяти.
Из этого возникает два вопроса - как сравнивать два значения Float? Как обеспечить точность при работе со значениями, где она критически важна (к примеру, с деньгами)?
Что касается сравнения, один из способов это использовать Delta Number. Это число можно рассматривать как показатель погрешности округления. Например, разница для операции 0,10 + 0,05 составляет приблизительно 0,0000000000000001. Разница должна быть больше или равна абсолютному значению результата вычитания ожидаемого и фактического значений.
То есть можно использовать следующий метод -
def assert_in_delta(exp, act, delta)
n = (exp - act).abs
delta >= n
end
assert_in_delta(0.15, (0.10 + 0.05), 0.0000000000000001)
=> true