Книга: Программирование на языке Ruby

5.4. Сравнение чисел с плавающей точкой

5.4. Сравнение чисел с плавающей точкой

Печально, но факт: в компьютере числа с плавающей точкой представляются неточно. В идеальном мире следующий код напечатал бы «да», но на всех машинах где мы его запускали, печатается «нет»:

x = 1000001.0/0.003
y = 0.003*x
if y == 1000001.0
 puts "да"
else
 puts "нет"
end

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

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

Ниже показан простой способ выполнения сравнения с «поправкой», когда числа считаются равными, если отличаются не более чем на величину, задаваемую программистом:

class Float
 EPSILON = 1e-6 # 0.000001
 def == (x)
  (self-x).abs < EPSILON
 end
end
x = 1000001.0/0.003
y = 0.003*x
if y == 1.0 # Пользуемся новым оператором ==.
 puts "да" # Теперь печатается "да".
else
 puts "нет"
end

В зависимости от ситуации может понадобиться задавать разные погрешности. Для этого определим в классе Float новый метод equals?. (При таком выборе имени мы избежим конфликта со стандартными методами equal? и eql?; последний, кстати, вообще не следует переопределять).

class Float
 EPSILON = 1e-6
 def equals?(x, tolerance=EPSILON)
  (self-x).abs < tolerance
 end
end
flag1 = (3.1416).equals? Math::PI # false
flag2 = (3.1416).equals?(Math::PI, 0.001) # true

Можно также ввести совершенно новый оператор для приближенного сравнения, назвав его, например, =~.

Имейте в виду, что это нельзя назвать настоящим решением. При последовательных вычислениях погрешность накапливается. Если вам совершенно необходимы числа с плавающей точкой, смиритесь с неточностями (см. также разделы 5.8 и 5.9).

Оглавление книги

Оглавление статьи/книги

Генерация: 1.352. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз