NaN导致Haskell中的大量距离,仅在功能评估中

tra*_*ard 1 haskell numbers

我似乎有一个距离公式,直接运行时工作正常,但不是作为一个函数运行时.我有:

data Point = Point Int Int
  deriving (Ord, Eq, Show)

distance :: Point -> Point -> Double

distance (Point x1 y1) (Point x2 y2) =
  sqrt $ fromIntegral $ ((x2 - x1) ^ 2) + ((y2 - y1) ^ 2)
Run Code Online (Sandbox Code Playgroud)

distance (Point 0 0) (Point 357356081635 896342957994)以ghci 运行产生NaN.但是,替换并直接运行会sqrt $ fromIntegral $ (357356081635 ^ 2) + (896342957994 ^ 2)产生一个有用的数值结果:9.649528835269391e11.

为什么?(谢谢,QuickCheck,找到这个!)

Car*_*ten 7

想一想.

你正在做正方形和减法Int- 所以你会溢出并得到负值而不是传递给平方根:

> let x1 = 0 :: Int
> let y1 = 0 :: Int
> let x2 = 357356081635 :: Int
> let y2 = 896342957994 :: Int
> ((x2-x1) ^ 2) + ((y2 - y1) ^ 2)
-2233181682604143571
Run Code Online (Sandbox Code Playgroud)

你可以看到你得到一个负数.

第二部分(在ghci中)足够聪明,可以采用正确的类型,因为你没有以任何方式限制它(尝试:t)

> :t fromIntegral $ (357356081635 ^ 2) + (896342957994 ^ 2)
fromIntegral $ (357356081635 ^ 2) + (896342957994 ^ 2) :: Num b => b

> :t sqrt $ fromIntegral $ (357356081635 ^ 2) + (896342957994 ^ 2)
sqrt $ fromIntegral $ (357356081635 ^ 2) + (896342957994 ^ 2) :: Floating a => a

> sqrt $ fromIntegral $ (357356081635 ^ 2) + (896342957994 ^ 2) :: Double
9.649528835269391e11
Run Code Online (Sandbox Code Playgroud)

你可以通过引入fromIntegral内部来完成这项工作:

> sqrt $ (fromIntegral x2 - fromIntegral x1)^2 + (fromIntegral y2 - fromIntegral y1) ^ 2
9.649528835269391e11   
Run Code Online (Sandbox Code Playgroud)

补充说明

Piezoid在下面的优秀评论中添加了这个:

要评估具有多态文字的表达式,ghci必须选择一种类型.在这种情况下,大数字用Integer表示,Integer是任意精度
整数的类型(以一些开销为代价).:set -fwarn-type-defaults当这种行为出现时,使用会在ghci中警告你.使用双打,如本答案中所建议的,是一个更好的解决方案,因为结果是Double.此外,函数hypot还有一个更好的计算路径:Wikipedia.

  • 要评估具有多态文字的表达式,ghci必须选择一种类型.在这种情况下,大数字用[`Integer`](http://hackage.haskell.org/package/base-4.7.0.0/docs/Prelude.html#t:Integer)表示,这是一种任意精度的类型整数(以一些开销为代价).当出现这种情况时,使用`:set -fwarn-type-defaults`会在ghci中警告你.使用双打,如本答案中所建议的,是一个更好的解决方案,因为结果是Double.[函数`hypot`:维基百科](http://en.wikipedia.org/wiki/Hypot)也有更好的计算路径. (4认同)