[Haskell-cafe] abs minBound < (0 :: Int) && negate minBound == (minBound :: Int)

Richard A. O'Keefe ok at cs.otago.ac.nz
Tue Aug 20 03:07:20 CEST 2013


On 20/08/2013, at 3:43 AM, Kyle Miller wrote:

> On Sun, Aug 18, 2013 at 8:04 PM, Richard A. O'Keefe <ok at cs.otago.ac.nz> wrote:
> The argument for twos-complement, which always puzzled me, is that the other
> systems have two ways to represent zero.  I never found this to be a problem,
> not even for bitwise operations, on the B6700.  I *did* find "abs x < 0"
> succeeding to be a pain in the posterior.  (The B6700 had two different tests:
> 'are these two numbers equal' and 'are these two bit patterns equal'.)
> 
> I think a better argument for twos complement is that you're just doing all of your computations modulo 2^n (where n is 32 or 64 or whatever), and addition and multiplication work as expected modulo anything.

To me, that's not a better argument.  It isn't even a _good_ argument.
It amounts to saying "if you do things wrong, you can justify it by
saying you're really doing something else right, and it's the programmer's
fault for wanting the wrong thing."

One great thing about the B6700 was that you were guaranteed
EITHER the mathematically correct answer in the numbers you were
thinking in terms of OR an exception telling you the machine couldn't
do what you wanted.  When it comes to *applications* programming,
the number of times I have *wanted* arithmetic modulo 2^n (in the last
40 years) can be counted on the fingers of one ear.

You may call it "multiplication work[ing] as expected" when the product of two
positive numbers comes out negative; I call it a wrong answer.

Prelude> let tens = 1 : map (*10) tens :: [Int]
Prelude> take 19 tens
[1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000,10000000000,100000000000,1000000000000,10000000000000,100000000000000,1000000000000000,10000000000000000,100000000000000000,1000000000000000000]
Prelude> [x * x | x <- take 19 tens]
[1,100,10000,1000000,100000000,10000000000,1000000000000,100000000000000,10000000000000000,1000000000000000000,7766279631452241920,1864712049423024128,2003764205206896640,-2537764290115403776,4477988020393345024,5076944270305263616,-8814407033341083648,4003012203950112768,-5527149226598858752]

Yes, I know that Haskell has Integer.
If I want to do more arithmetic than a bit of simple counting,
I like to use it.
The gibberish that Int multiplication spewed out above is why.

Roughly speaking, there are three ways to handle integer
arithmetic: the Lisp way, the Ada way, and the Java way.
Lisp just gets it right (think Haskell's "Integer" type).
Java *defines* wrong answers to be "right".
Ada recognises that sometimes you want modular arithmetic (so it offers you
modular types) and sometimes you don't (so it offers you bounded but
non-modular types, where overflow is trapped).

Even the C standard is careful to allow signed arithmetic to trap overflows.

When we tell our students that there is an integer N > 0 such that N+1 < 0,
first they are shocked and confused, then they forget about it, and then
they are caught by it, and at last they remember it, but aren't sure what
they can _do_ about it in Java.





More information about the Haskell-Cafe mailing list