代码之家  ›  专栏  ›  技术社区  ›  David Miani

显示与unsafeCorced值一起使用时返回错误的值

  •  5
  • David Miani  · 技术社区  · 12 年前

    我在试验 unsafeCoerce 具有 Int8 Word8 ,我发现了一些令人惊讶的行为(无论如何对我来说)。

    文字8 是一个范围在0-255之间的8位无符号数字。 国际8 是一个有符号的8位数字,范围从-128..127。

    由于它们都是8位数字,我认为将一个强制为另一个是安全的,只需返回8位值,就好像它是有符号/无符号的一样。

    例如 unsafeCoerce (-1 :: Int8) :: Word8 我希望结果 文字8 255的值(因为有符号int中-1的位表示与无符号int中的255相同)。

    然而,当我执行强制时 文字8 这种行为很奇怪:

    > GHCi, version 7.4.1: http://www.haskell.org/ghc/  :? for help
    > import Data.Int
    > import Data.Word
    > import Unsafe.Coerce
    > class ShowType a where typeName :: a -> String
    > instance ShowType Int8 where typeName _ = "Int8"
    > instance ShowType Word8 where typeName _ = "Word8"
    
    > let x = unsafeCoerce (-1 :: Int8) :: Word8
    > show x
    "-1"
    > typeName x
    "Word8"
    > show (x + 0)
    "255"
    > :t x
    x :: Word8
    > :t (x + 0)
    (x + 0) :: Word8
    

    我不明白怎么回事 show x 正在返回 "-1" 在这里如果你看 map show [minBound..maxBound :: Word8] ,没有可能的值 文字8 结果在 "-1" 此外,即使类型没有改变,在数字上加0是如何改变行为的?奇怪的是,它似乎只是 Show 受影响的类-我的 ShowType 类返回正确的值。

    最后,代码 fromIntegral (-1 :: Int8) :: Word8 按预期工作,返回255,并正确使用 show .编译器是否可以将此代码简化为无操作?

    请注意,这个问题只是出于对ghc中类型如何在低水平上表示的好奇。实际上,我并没有在代码中使用unsafeCorce。

    2 回复  |  直到 12 年前
        1
  •  10
  •   Mikhail Glushenkov    12 年前

    正如@kosmikus所说,两者 Int8 Int16 使用 Int# ,在32位体系结构上为32位宽(以及 Word8 Word16 Word# 发动机罩下)。 This comment 在GHC中。Prim对此进行了更详细的解释。

    因此,让我们来了解为什么这种实现选择会导致您看到的行为:

    > let x = unsafeCoerce (-1 :: Int8) :: Word8
    > show x
    "-1"
    

    这个 Show 的实例 文字8 is defined as

    instance Show Word8 where
        showsPrec p x = showsPrec p (fromIntegral x :: Int)
    

    fromIntegral 只是 fromInteger . toInteger .定义 toInteger 对于 文字8

    toInteger (W8# x#)            = smallInteger (word2Int# x#)
    

    哪里 smallInteger (以整数gmp定义)为

    smallInteger :: Int# -> Integer
    smallInteger i = S# i
    

    word2Int# 是一个 primop 带类型 Word# -> Int# -的类似物 reinterpret_cast<int> 在C++中。所以这就解释了你为什么看到 -1 在第一个例子中:该值只是被重新解释为一个有符号的整数并打印出来。

    现在,为什么要添加 0 x 给你 255 ? 看着 Num 的实例 文字8 我们看到:

    (W8# x#) + (W8# y#)    = W8# (narrow8Word# (x# `plusWord#` y#))
    

    所以看起来 narrow8Word# 普里莫普是罪魁祸首。让我们检查一下:

    > import GHC.Word
    > import GHC.Prim
    > case x of (W8# w) -> (W8# (narrow8Word# w))
    255
    

    确实如此。这就解释了为什么添加0不是不操作- 文字8 加法实际上将该值压缩到了预期范围。

        2
  •  4
  •   augustss    12 年前

    当你用过的时候,你不能说有什么不对 unsafeCoerce 。如果你使用这个函数,任何事情都可能发生。编译器可能存储 Int8 总之,并使用 非安全强制 Word8 打破这个单词中存储的不变量。使用 fromIntegral 转换。

    转换自 国际8 文字8 使用 来自积分 变成一个 movzbl 在x86上使用ghc的指令,这基本上是一个no-op。