我首先定义一个大整数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
的最佳方法是什么?
答案 0 :(得分:14)
不是floor
失去精度,而是从Integer
(任意精度整数)到Double
的转换(浮点值,精度有限) 。因此,fromIntegral n :: Double
不再与n
相同。
Double
有一个53位的尾数(52显式存储,前导隐含),大约相当于16位十进制数。因此,只有结果的(大约)16个最高有效位有效。其余的只是噪音。
最后,您的前两个比较比较Double
s;并n
转换到Double
,s2
已转换到Double
,s1
等于。但是,在第三次比较中,n
和s2
都是Integer
s;它们可以比较为Integer
s,因此对它们调用fromIntegral
是无操作,并且它们的非转换整数值是不同的。如果您强制转换为Double
,则值会再次变为相等:
Prelude> ((fromIntegral n) :: Double) == ((fromIntegral s2) :: Double)
True