Haskell浮点数计算错误
Haskell float calculation error
我正在尝试编写一个计算曲线下面积的简短函数,但我一直收到类型不匹配错误。
以下函数中的参数:l
、r
为求值范围,a
、b
为曲线参数。
solve :: Int -> Int -> [Int] -> [Int] -> [Double]
solve l r a b = [area]
where eval a b p = fromIntegral . sum . map (\(ai, bi) -> ai * p ^ bi) $ zip a b
area = foldl (\acc p -> acc + 0.001 * eval a b p) 0 range
range = map (\a -> (fromIntegral a :: Double ) / 1000) [l*1000..r*1000]
我感到非常沮丧,因为 Haskell 中的类型系统真的不是那么直观。有人可以建议在数值计算中处理浮点数时的最佳做法吗?
综上所述,上面的代码不起作用,因为:
- 在类型声明中
a
声明为 [Int]
- 因此 Haskell 推断
eval
也有类型 Int
,因为 (*)
有签名 Num a => a -> a -> a
(所以它只接受同类型)
如果我们想在不改变输入类型的情况下将这道题中的代数曲线计算为浮点值,我们可以将a
转换为[Double]
。这是代码:
solve :: Int -> Int -> [Int] -> [Int] -> [Double]
solve l r a b = [area]
where eval p = sum . map (\(ai, bi) -> ai * p ^^ bi) $ zip af b
area = foldl (\acc p -> acc + 0.001 * eval p) 0 range
range = map (\x -> (fromIntegral x :: Double ) / 1000) [l*1000..r*1000]
af = map fromIntegral a :: [Double]
我还将 ^
更改为 ^^
以处理负指数。
要了解类型错误,您应该了解一些类型 :)。一个好的起点是注释掉类型声明以查看 GHCi 推断的内容(在这种情况下,我们的错误不会改变,但确保我们的类型声明不是问题是一个很好的通用做法)。无论如何,当我们这样做时,我们遇到错误:
floatingError.hs:4:47: error:
• No instance for (Integral Double) arising from a use of ‘eval’
• In the second argument of ‘(*)’, namely ‘eval a b p’
In the second argument of ‘(+)’, namely ‘0.001 * eval a b p’
In the expression: acc + 0.001 * eval a b p
我们从相关数字运算符 (*)
和 (+)
的类型签名中得知它们接受相同类型的参数。这是我们错误的原因; eval
应该是整型。但是,我们对其应用了 fromIntegral
函数。因此,如果我们删除它,我们的函数应用程序会进行类型检查并且我们的程序会编译。
接下来,我们可以检查 GHCi 为 solve
推断出的类型签名:
solve :: (Integral b, Integral t) =>
t -> t -> [Double] -> [b] -> [Double]
因为 Int
有一个类型类 Integral
的实例,我们知道我们声明的签名不会与我们修改的函数定义冲突。
我们的最终代码:
solve :: Int -> Int -> [Double] -> [Int] -> [Double]
solve l r a b = [area]
where eval a b p = sum . map (\(ai, bi) -> ai * p ^ bi) $ zip a b
area = foldl (\acc p -> acc + 0.001 * eval a b p) 0 range
range = map (\a -> (fromIntegral a :: Double ) / 1000) [l*1000..r*1000]
我正在尝试编写一个计算曲线下面积的简短函数,但我一直收到类型不匹配错误。
以下函数中的参数:l
、r
为求值范围,a
、b
为曲线参数。
solve :: Int -> Int -> [Int] -> [Int] -> [Double]
solve l r a b = [area]
where eval a b p = fromIntegral . sum . map (\(ai, bi) -> ai * p ^ bi) $ zip a b
area = foldl (\acc p -> acc + 0.001 * eval a b p) 0 range
range = map (\a -> (fromIntegral a :: Double ) / 1000) [l*1000..r*1000]
我感到非常沮丧,因为 Haskell 中的类型系统真的不是那么直观。有人可以建议在数值计算中处理浮点数时的最佳做法吗?
综上所述,上面的代码不起作用,因为:
- 在类型声明中
a
声明为[Int]
- 因此 Haskell 推断
eval
也有类型Int
,因为(*)
有签名Num a => a -> a -> a
(所以它只接受同类型)
如果我们想在不改变输入类型的情况下将这道题中的代数曲线计算为浮点值,我们可以将a
转换为[Double]
。这是代码:
solve :: Int -> Int -> [Int] -> [Int] -> [Double]
solve l r a b = [area]
where eval p = sum . map (\(ai, bi) -> ai * p ^^ bi) $ zip af b
area = foldl (\acc p -> acc + 0.001 * eval p) 0 range
range = map (\x -> (fromIntegral x :: Double ) / 1000) [l*1000..r*1000]
af = map fromIntegral a :: [Double]
我还将 ^
更改为 ^^
以处理负指数。
要了解类型错误,您应该了解一些类型 :)。一个好的起点是注释掉类型声明以查看 GHCi 推断的内容(在这种情况下,我们的错误不会改变,但确保我们的类型声明不是问题是一个很好的通用做法)。无论如何,当我们这样做时,我们遇到错误:
floatingError.hs:4:47: error: • No instance for (Integral Double) arising from a use of ‘eval’ • In the second argument of ‘(*)’, namely ‘eval a b p’ In the second argument of ‘(+)’, namely ‘0.001 * eval a b p’ In the expression: acc + 0.001 * eval a b p
我们从相关数字运算符 (*)
和 (+)
的类型签名中得知它们接受相同类型的参数。这是我们错误的原因; eval
应该是整型。但是,我们对其应用了 fromIntegral
函数。因此,如果我们删除它,我们的函数应用程序会进行类型检查并且我们的程序会编译。
接下来,我们可以检查 GHCi 为 solve
推断出的类型签名:
solve :: (Integral b, Integral t) =>
t -> t -> [Double] -> [b] -> [Double]
因为 Int
有一个类型类 Integral
的实例,我们知道我们声明的签名不会与我们修改的函数定义冲突。
我们的最终代码:
solve :: Int -> Int -> [Double] -> [Int] -> [Double]
solve l r a b = [area]
where eval a b p = sum . map (\(ai, bi) -> ai * p ^ bi) $ zip a b
area = foldl (\acc p -> acc + 0.001 * eval a b p) 0 range
range = map (\a -> (fromIntegral a :: Double ) / 1000) [l*1000..r*1000]