assert терпит неудачу, когда это не должно, в тестовом примере Smalltalk Unit

Я в тупике. Вот мой тестовый пример.

theTestArray := #(1.2 3 5.1 7).
self assert: theTestArray  squareOfAllElements = #(1.44 9 26.01 49).

Утверждение не должно подвести. По расчету квадрат каждого элемента является правильным. Итак, я сделал "шаг в тест", показывает, что результат метода squareOfAllElements и #(1.44 9 26.01 49) оба одинаковы, но assert оценивается как false. Зачем? Что я здесь не так делаю? Любая помощь приветствуется.

3 ответа

Решение

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

Подробности см. В разделе 1.1 проекта главы о числах с плавающей запятой в Pharo на примере: http://stephane.ducasse.free.fr/Web/Draft/Float.pdf

Как сказано в других ответах, Float неточны. Также помните, что в Visualworks Float по умолчанию используется одинарная точность (около 7 десятичных знаков), если вы добавите число с плавающей запятой к букве d, например, 5.1d, вы получите двойную точность (около 15 десятичных знаков), менее неточную, но все же неточную.

Еще одним источником путаницы является то, что два разных Float могут печатать с одинаковым приблизительным десятичным представлением в Visualworks.

5.1 squared printString
-> '26.01'

но

5.1 squared = 26.01
-> false

Обратите внимание, что последние Squeak или Pharo печатают достаточно десятичных знаков, чтобы различать различные значения с плавающей точкой (и интерпретировать их без изменений).

5.1 squared
->26.009999999999998

Кроме того, вы можете использовать так называемую FixedPoint (в VisualWorks или ScaledDecimals в других вариантах) для выполнения точных операций:

theTestArray := #(1.2s 3 5.1s 7).
self assert: theTestArray  squareOfAllElements = #(1.44s 9 26.01s 49).

Также остерегайтесь этой другой ловушки: FixedPoint (ScaledDecimals) печатает только столько десятичных знаков после точки дроби, сколько ему было сказано, но внутренне он может содержать больше (бесконечно много).

5.1s1 squared printString
-> '26.0s1'

но

5.1s1 squared = 26.01s2
-> true

Однако сообщение равенства сравнения #= отправляется в коллекцию, предположительно возвращаемую #squareOfAllElements.

Вы можете переписать свой тестовый оператор как:

theTestArray := #(1.2 3 5.1 7).
theSquaredArray := theTestArray collect: [:each | each squared].
theTestArray  with: theSquaredArray do: [:a :b | self assert: (a equals: b) ].

Это будет проверять то же, что и предыдущий, но будет запускать один #assert: per element.

Другой вариант - реализовать вариант #hasEqualElements: с плавающей запятой >># равный: вместо #=.

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