Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this code divide by zero?

I have a small Haskell program, and am curious why a divide by zero exception gets thrown when I run it (GHC 7.0.3)

import qualified Data.ByteString.Lazy as B
import Codec.Utils

convert :: B.ByteString -> [Octet]
convert bs = map (head . toTwosComp) $ B.unpack bs

main = putStrLn $ show $ convert $ B.pack [1, 2, 3, 4]

Can anyone help me understand what's going on here?

like image 301
Litherum Avatar asked Dec 17 '11 07:12

Litherum


People also ask

Why is a number divided by 0?

Because what happens is that if we can say that zero, 5, or basically any number, then that means that that "c" is not unique. So, in this scenario the first part doesn't work. So, that means that this is going to be undefined. So zero divided by zero is undefined.

What happens when you divide by zero in programming?

So, when something is divided by zero, mathematicians simply state that the answer is 'undefined'. This is not a totally abstract idea as you can see it in action in the real world. Computer programmers who accidentally divide by zero will get their code stuck in an infinite loop, for instance.

Why is divide by zero an error?

The short answer is that 0 has no multiplicative inverse, and any attempt to define a real number as the multiplicative inverse of 0 would result in the contradiction 0 = 1.


1 Answers

We can reduce this to

GHCi> toTwosComp (1 :: Word8)
*** Exception: divide by zero

Note that this works if you use Word16, Int, Integer, or any number of types, but fails when using Word8, as B.unpack gives us! So why does it fail? The answer is found in the source code to Codec.Utils.toTwosComp. You can see that it calls toBase 256 (abs x), where x is the argument.

The type of toBase — not exported from the Codec.Utils module, and without an explicit type signature in the source, but you can see this by putting the definition in a file and asking GHCi what the type is (:t toBase), is

toBase :: (Integral a, Num b) => a -> a -> [b]

So, annotating types explicitly, toTwosComp is calling toBase (256 :: Word8) (abs x :: Word8). What's 256 :: Word8?

GHCi> 256 :: Word8
0

Oops! 256 > 255, so we can't hold it in a Word8, and it overflows silently. toBase, in the process of its base conversion, divides by the base being used, so it ends up dividing by zero, producing the behaviour you're getting.

What's the solution? Convert the Word8s to Ints with fromIntegral before passing them to toTwosComp:

convert :: B.ByteString -> [Octet]
convert = map convert' . B.unpack
  where convert' b = head $ toTwosComp (fromIntegral b :: Int)

Personally, this behaviour worries me a bit, and I think toTwosComp should probably do such a conversion itself, likely to Integer, so that it works with integral types of every size; but this would incur a performance penalty that the developers might not like the idea of. Still, this is a pretty confusing failure that requires source-diving to understand. Thankfully, it's very easy to work around.

like image 84
ehird Avatar answered Oct 05 '22 12:10

ehird