2013-04-05 6 views
5

Eksperymentowałem z unsafeCoerce z Int8 i Word8 i znalazłem zaskakujące zachowanie (dla mnie w każdym razie).wyświetla powracającą niewłaściwą wartość, gdy jest używana z niebezpieczną wartością wymuszoną

Word8 to 8-bitowa liczba bez znaku, która mieści się w zakresie od 0 do 255. Int8 to podpisany 8-bitowy numer w zakresie od -128..127.

Ponieważ oba są liczbami 8-bitowymi, zakładałem, że wymuszanie jednego na drugim będzie bezpieczne i po prostu zwrócę 8-bitowe wartości, tak jakby były podpisane/niepodpisane.

Na przykład: unsafeCoerce (-1 :: Int8) :: Word8 Spodziewam się uzyskać wartość Word8 z 255 (ponieważ reprezentacja bitowa -1 w podpisanym int jest taka sama jak 255 w unsigned int).

Jednak, kiedy zrobić wykonać zmusić The Word8 zachowanie jest dziwne:

> 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 

Nie rozumiem jak show x wraca "-1" tutaj. Jeśli spojrzysz na map show [minBound..maxBound :: Word8], żadna z możliwych wartości dla Word8 nie zostanie znaleziona w "-1". Ponadto, w jaki sposób dodanie 0 do liczby zmienia zachowanie, nawet jeśli typ nie został zmieniony? O dziwo, wygląda na to, że dotyczy to tylko klasy Show - moja klasa ShowType zwraca poprawną wartość.

Wreszcie kod fromIntegral (-1 :: Int8) :: Word8 działa zgodnie z oczekiwaniami, a zwraca 255 i działa poprawnie z show. Czy ten kod może zostać zredukowany do kompilacji przez kompilator?

Należy zauważyć, że to pytanie jest tylko z ciekawości, jak typy są reprezentowane w ghc na niskim poziomie. W rzeczywistości nie używam w moim kodzie niebezpiecznegoCo commerce.

Odpowiedz

10

jak @kosmikus że zarówno Int8 i Int16 są realizowane przy użyciu Int#, który jest 32-bitową, o szerokości 32-bitowych (i Word8 i Word16Word# pod wyciągiem). This comment w GHC.Prim wyjaśnia to bardziej szczegółowo.

Warto więc dowiedzieć się, dlaczego ta implementacja wyniki Choice w zachowaniu widać:

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

Instancja Show dla Word8is defined as

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

i fromIntegral tylko fromInteger . toInteger.Definicja toInteger do Word8 jest

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

gdzie smallInteger (zdefiniowane w całkowitej GMP) jest

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

i word2Int# jest primop z typem Word# -> Int# - analogiem reinterpret_cast<int> C++. To wyjaśnia, dlaczego w pierwszym przykładzie widzisz -1: wartość jest po prostu reinterpretowana jako liczba całkowita ze znakiem i drukowana.

Dlaczego dodawanie 0 do x daje 255? Patrząc na przykład Num dla Word8 widzimy to:

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

Tak to wygląda narrow8Word# primop jest winowajcą. Sprawdźmy:

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

Rzeczywiście tak. To tłumaczy, dlaczego dodanie 0 nie jest funkcją "no-op" - w rzeczywistości faktycznie ogranicza wartość do zamierzonego zakresu.

+0

Dzięki temu ma teraz sens! –

4

Nie możesz powiedzieć, że coś jest nie tak, kiedy używasz unsafeCoerce. Wszystko może się zdarzyć, jeśli użyjesz tej funkcji. Kompilator prawdopodobnie przechowuje w jednym słowie słowo Int8, a użycie unsafeCoerce do Word8 łamie niezmienniki tego, co jest przechowywane w tym słowie. Użyj konwersji fromIntegral.

Konwersja z Int8 do Word8 użyciu fromIntegral zamienia się movzbl instrukcją użyciem GHC na x86, który jest w zasadzie nie-op.

+0

Rozumiem, że wszystko może się zdarzyć, ale chciałbym wiedzieć, co dzieje się pod maską. Chyba że 'Word8' zużywa więcej niż 1 bajt, gdy jest używany w haskell, nie mogę zrozumieć, jak możesz uzyskać to zachowanie z' unsafeCoerce'. 'length [minBound..maxBound :: Word8]' wynosi 256, tyle samo kombinacji co w bajcie. Jak więc mogą istnieć jakieś nieprawidłowe wartości? Czy istnieje sposób, aby zrzutu surowej wartości wartości w haskell, aby zobaczyć, co się dzieje? –

+3

Jak mówi Lennart, 'Int8' jest najprawdopodobniej zapisane w słowie. Na przykład na maszynie 64-bitowej otrzymuję '" -1 "' dla 'show (unsafeCoerce (-1 :: Int8) :: Word32)', ale '" 18446744073709551615 "' dla 'show (unsafeCoerce (- 1 :: Int8) :: Word64) '. – kosmikus

Powiązane problemy