Почему пол теряет точность и как это влияет на транзитивность равенства?
Я начинаю с определения большого целого числа 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 наиболее значимых цифр результата. Остальное просто шум.
Наконец, ваши первые два сравнения сравнивают Double
s; а также n
превращается в Double
, s2
превращается в Double
, а также s1
все равны В третьем сравнении, однако, n
а также s2
оба Integer
s; их можно сравнить как Integer
с, так зовет fromIntegral
на них нет операции, и их не преобразованные целочисленные значения различны. Если вы заставите преобразование в Double
, значения снова становятся равными:
Prelude> ((fromIntegral n) :: Double) == ((fromIntegral s2) :: Double)
True