Почему пол теряет точность и как это влияет на транзитивность равенства?

Я начинаю с определения большого целого числа n:

Prelude> let n = 5705979550618670446308578858542675373983
Prelude> n :: Integer
5705979550618670446308578858542675373983

Затем я посмотрел на поведение s1 а также s2:

Prelude> let s1 = (sqrt (fromIntegral n))^2
Prelude> let s2 = (floor(sqrt(fromIntegral n)))^2

Prelude> s1 == fromIntegral n
True
Prelude> s1 == fromIntegral s2
True
Prelude> (fromIntegral n) == (fromIntegral s2)
False

Поскольку любая дробная часть может быть отброшена, равенство в последних 2 выражениях не ожидалось. Однако я не ожидал, что равенство будет непереходным (например, n == s1, s1 == s2, но n != s2.)

Более того, floor кажется, теряет точность в целочисленной части, несмотря на сохранение 40 значащих цифр.

Prelude> s1
5.70597955061867e39

Prelude> s2
5705979550618669899723442048678773129216

Эта потеря точности становится очевидной при тестировании вычитания:

Prelude> (fromIntegral n) - s1
0.0

Prelude> (fromIntegral n) - (fromIntegral s2)
546585136809863902244767

Почему floor потерять точность, и как это нарушает транзитивность равенства (если вообще)?

Каков наилучший подход к вычислениям? floor . sqrt без потери точности?

1 ответ

Решение

Это не floor это теряет точность, но преобразование из Integer (целое число произвольной точности) Double (значение с плавающей точкой, которое имеет ограниченную точность). Соответственно, fromIntegral n :: Double больше не совпадает со значением n,

Double имеет 53-битную мантиссу (52 явно хранятся, ведущая - неявная), что примерно эквивалентно 16 десятичным цифрам. Таким образом, действительными являются только (приблизительно) 16 наиболее значимых цифр результата. Остальное просто шум.

Наконец, ваши первые два сравнения сравнивают Doubles; а также n превращается в Double, s2 превращается в Double, а также s1 все равны В третьем сравнении, однако, n а также s2 оба Integers; их можно сравнить как Integerс, так зовет fromIntegral на них нет операции, и их не преобразованные целочисленные значения различны. Если вы заставите преобразование в Double, значения снова становятся равными:

Prelude> ((fromIntegral n) :: Double) == ((fromIntegral s2) :: Double)
True
Другие вопросы по тегам