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: с плавающей запятой >># равный: вместо #=.