Chúng ta có thể giảm xuống còn
GHCi> toTwosComp (1 :: Word8)
*** Exception: divide by zero
Lưu ý rằng công trình này nếu bạn sử dụng Word16, Int, Integer, hoặc bất kỳ số lượng các loại, nhưng không thành công khi sử dụng Word8, như B.unpack
cho chúng ta! Vậy tại sao nó lại thất bại? Câu trả lời được tìm thấy trong số source code đến Codec.Utils.toTwosComp. Bạn có thể thấy rằng nó gọi số toBase 256 (abs x)
, trong đó x là đối số.
Loại toBase
- không được xuất từ mô-đun Codec.Utils và không có chữ ký rõ ràng trong nguồn, nhưng bạn có thể thấy điều này bằng cách đặt định nghĩa trong tệp và yêu cầu GHCi loại đó là gì (:t toBase
) , là
toBase :: (Integral a, Num b) => a -> a -> [b]
Vì vậy, chú thích loại rõ ràng, toTwosComp
đang gọi toBase (256 :: Word8) (abs x :: Word8)
. 256 :: Word8
là gì?
GHCi> 256 :: Word8
0
Rất tiếc! 256> 255, vì vậy chúng tôi không thể giữ nó trong một Word8, và nó tràn âm thầm. toBase
, trong quá trình chuyển đổi cơ sở của nó, chia cho các cơ sở đang được sử dụng, do đó, nó kết thúc lên chia cho số không, sản xuất các hành vi bạn đang nhận được.
Giải pháp là gì? Chuyển đổi Word8s để Ints với fromIntegral
trước khi đi qua họ toTwosComp
:
convert :: B.ByteString -> [Octet]
convert = map convert' . B.unpack
where convert' b = head $ toTwosComp (fromIntegral b :: Int)
Cá nhân, hành vi này làm tôi lo lắng một chút, và tôi nghĩ rằng toTwosComp
có lẽ nên làm như vậy một chuyển đổi bản thân, khả năng Integer, để nó làm việc với loại tích phân của mọi kích thước; nhưng điều này sẽ phải chịu một hình phạt hiệu suất mà các nhà phát triển có thể không thích ý tưởng. Tuy nhiên, đây là một thất bại khá khó hiểu đòi hỏi nguồn lặn để hiểu. Rất may, nó rất dễ dàng để làm việc xung quanh.
Đó là lỗi trong 'toTwosComp'. – augustss