【问题标题】:Why does this code divide by zero?为什么此代码除以零?
【发布时间】:2011-12-17 07:27:05
【问题描述】:

我有一个小型 Haskell 程序,我很好奇为什么在运行它时会引发除以零异常 (GHC 7.0.3)

import qualified Data.ByteString.Lazy as B
import Codec.Utils

convert :: B.ByteString -> [Octet]
convert bs = map (head . toTwosComp) $ B.unpack bs

main = putStrLn $ show $ convert $ B.pack [1, 2, 3, 4]

谁能帮我理解这里发生了什么?

【问题讨论】:

    标签: haskell divide-by-zero


    【解决方案1】:

    我们可以将其简化为

    GHCi> toTwosComp (1 :: Word8)
    *** Exception: divide by zero
    

    请注意,如果您使用 Word16、Int、Integer 或任意数量的类型,这将有效,但在使用 Word8 时会失败,因为 B.unpack 给了我们!那么为什么会失败呢?答案在source codeCodec.Utils.toTwosComp 中找到。可以看到它调用了toBase 256 (abs x),其中x是参数。

    toBase 的类型——不是从 Codec.Utils 模块导出的,并且在源代码中没有明确的类型签名,但是你可以通过将定义放在一个文件中并询问 GHCi 类型是什么来看到这一点(@ 987654327@), 是

    toBase :: (Integral a, Num b) => a -> a -> [b]
    

    因此,显式注释类型,toTwosComp 正在调用toBase (256 :: Word8) (abs x :: Word8)256 :: Word8 是什么?

    GHCi> 256 :: Word8
    0
    

    哎呀! 256 > 255,所以我们不能把它放在一个 Word8 中,它会无声地溢出。 toBase,在它的基数转换过程中,除以正在使用的基数,所以它最终被零除,产生你得到的行为。

    解决办法是什么?使用fromIntegral 将Word8s 转换为Ints,然后将它们传递给toTwosComp

    convert :: B.ByteString -> [Octet]
    convert = map convert' . B.unpack
      where convert' b = head $ toTwosComp (fromIntegral b :: Int)
    

    就个人而言,这种行为让我有点担心,我认为toTwosComp 应该自己进行这样的转换,可能是整数,以便它适用于各种大小的整数类型;但这会导致开发人员可能不喜欢这个想法的性能损失。尽管如此,这是一个非常令人困惑的失败,需要源头潜水才能理解。值得庆幸的是,这很容易解决。

    【讨论】:

    • 超级有帮助!非常感谢:D
    【解决方案2】:
    map (head . toTwosComp) [1, 2, 3, 4]
    

    工作正常

    map (head . toTwosComp) $ B.unpack $ B.pack [1, 2, 3, 4]
    

    导致您描述的异常。让我们看看有什么不同。

    > :t [1, 2, 3, 4]
    [1, 2, 3, 4] :: Num t => [t]
    > :t unpack $ pack $ [1, 2, 3, 4]
    unpack $ pack $ [1,2,3,4] :: [Word8]
    

    Word8 可能会导致问题。让我们看看

    > toTwosComp (1 :: Word8)
    *** Exception: divide by zero
    

    显然我们必须将 Word8 转换为其他整数类型。

    > map (head . toTwosComp . fromIntegral) $ B.unpack $ B.pack [1, 2, 3, 4]
    [1,2,3,4]
    

    有效!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-19
      • 2013-02-02
      相关资源
      最近更新 更多