Подтвердить что ты не робот

Unittest (иногда) терпит неудачу из-за неточности с плавающей запятой

У меня есть класс Vector, который представляет точку в 3-мерном пространстве. Этот вектор имеет метод normalize(self, length = 1), который масштабирует вектор вниз/вверх до length == vec.normalize(length).length.

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



Дополнительная информация:

    def testNormalize(self):
        vec = Vector(random.random(), random.random(), random.random())
        self.assertEqual(vec.normalize(5).length, 5)

Этот иногда приводит к AssertionError: 4.999999999999999 != 5 или AssertionError: 5.000000000000001 != 5.

Примечание. Я знаю, что проблема с плавающей запятой может быть в свойстве Vector.length или в Vector.normalize().

4b9b3361

Ответ 1

1) Как я могу убедиться, что тест работает?

Используйте assertAlmostEqual, assertNotAlmostEqual.

Из официальной документации :

assertAlmostEqual(first, second, places=7, msg=None, delta=None)

Проверить, что первая и вторая приблизительно равны, вычисляя разность, округляя до заданного числа десятичных знаков (по умолчанию 7) и сравнивая с нулем.

2) Можно ли это сделать без проверки приблизительного значения?

В качестве альтернативы нет.

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

Ответ 2

Используя значение с плавающей запятой, вы принимаете небольшую возможную неточность. Поэтому ваши тесты должны проверить, попадает ли вычисленное значение в допустимый диапазон, например:

theoreticalValue - epsilon < normalizedValue < theoreticalValue + epsilon

где epsilon - очень маленькое значение, которое вы определяете как приемлемое для вариации из-за неточности с плавающей запятой.

Ответ 3

В общем, вы не должны утверждать равенство для float. Вместо этого убедитесь, что результат находится в определенных границах, например:

self.assertTrue(abs(vec.normalize(5).length - 5) < 0.001)

Ответ 4

Я полагаю, что одна из возможностей заключается в применении функции для проверки случаев, для которых все входы, результаты всех промежуточных вычислений и вывод точно представлены через float.

Чтобы проиллюстрировать:

In [2]: import math

In [4]: def norm(x, y):
   ...:     return math.sqrt(x*x + y*y)
   ...: 

In [6]: norm(3, 4) == 5
Out[6]: True

Не уверен, насколько это практично...