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

时间:2013-04-05 08:11:58

标签: haskell ghc coercion

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

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

因为它们都是8位数,所以我认为相互强制转换是安全的,只需返回8位值,就好像它是有符号/无符号的一样。

例如,unsafeCoerce (-1 :: Int8) :: Word8我希望导致Word8值为255(因为有符号整数中的位表示-1与无符号整数中的255相同)。 / p>

但是,当我执行强制操作时,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],则Word8的可能值不会导致"-1"。此外,即使类型未更改,如何向数字添加0也会更改行为?奇怪的是,它似乎只有Show类受到影响 - 我的ShowType类返回正确的值。

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

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

2 个答案:

答案 0 :(得分:10)

就像@kosmikus所说,Int8Int16都是使用Int#实现的,在32位架构上是32位宽的(Word8和{ {1}}是Word16。 GHC.Prim中的This comment更详细地解释了这一点。

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

Word#

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

Show个实例
Word8

fromIntegral只是instance Show Word8 where showsPrec p x = showsPrec p (fromIntegral x :: Int) fromInteger . toInteger toInteger的定义是

Word8

其中toInteger (W8# x#) = smallInteger (word2Int# x#) (在integer-gmp中定义)是

smallInteger

smallInteger :: Int# -> Integer smallInteger i = S# i primop,其类型为word2Int# - 在C ++中类似于Word# -> Int#。这就解释了为什么在第一个示例中看到reinterpret_cast<int>的原因:该值只是重新解释为有符号整数并打印出来。

现在,为什么将-1添加到0会给你x?查看255的{​​{1}}个实例,我们看到了这一点:

Num

所以看起来Word8 primop是罪魁祸首。我们来看看:

(W8# x#) + (W8# y#)    = W8# (narrow8Word# (x# `plusWord#` y#))
确实如此。这就解释了为什么添加0不是无操作 - narrow8Word#添加实际上会将值限制在预期范围内。

答案 1 :(得分:4)

当你使用unsafeCoerce时,你不能说出错了。如果您使用该功能,任何事情都可能发生。编译器可能在单词中存储Int8,并使用unsafeCoerceWord8打破存储在该单词中的不变量。使用fromIntegral进行转换。

使用Int8Word8转换为fromIntegral会在x86上使用ghc转换为movzbl指令,这基本上是无操作。