5

我正在尝试使用unsafeCoerceand Int8Word8我发现了一些令人惊讶的行为(无论如何对我来说)。

Word8是一个 8 位无符号数,范围为 0-255。Int8是一个有符号的 8 位数字,范围为 -128..127。

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

例如,unsafeCoerce (-1 :: Int8) :: Word8我希望得到一个Word8255 的值(因为 -1 在有符号整数中的位表示与无符号整数中的 255 相同)。

但是,当我执行强制时,Word8行为很奇怪:

> 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]没有可能的值。此外,即使类型没有改变,向数字添加 0 会如何改变行为?奇怪的是,似乎只有类受到影响——我的类返回正确的值。Word8"-1"ShowShowType

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

请注意,这个问题只是出于对类型在 ghc 中的低级别表示方式的好奇。我实际上并没有在我的代码中使用 unsafeCoerce。

4

2 回答 2

10

就像@kosmikus 所说,两者Int8Int16都是使用 实现的Int#,它在 32 位架构上是 32 位宽的(并且Word8Word16引擎盖Word#下)。GHC.Prim 中的这条评论更详细地解释了这一点。

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

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

Show实例Word8 定义为

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

并且fromIntegral只是fromInteger . toIntegertoIntegerfor的定义Word8

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

其中smallInteger(在 integer-gmp 中定义)是

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

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

现在,为什么要加0x255?查看Num实例,Word8我们看到:

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

所以看起来narrow8Word#primop是罪魁祸首。让我们检查:

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

它的确是。这就解释了为什么添加 0 不是无操作 -Word8添加实际上将值限制在预期范围内。

于 2013-04-05T13:46:35.517 回答
4

用过之后不能说有什么问题unsafeCoerce。如果您使用该功能,任何事情都可能发生。编译器可能将 a 存储Int8在一个单词中,并使用unsafeCoerce它来Word8破坏该单词中存储的内容的不变量。用于fromIntegral转换。

Int8Word8using的转换fromIntegral变成了movzbl在 x86 上使用 ghc 的指令,这基本上是一个空操作。

于 2013-04-05T08:41:23.230 回答