
Strange multiplication behavior in Guile Scheme interpreter - schh
http://stackoverflow.com/questions/14495636/strange-multiplication-behavior-in-guile-scheme-interpreter
======
wingo
[http://git.savannah.gnu.org/gitweb/?p=guile.git;a=commit;h=2...](http://git.savannah.gnu.org/gitweb/?p=guile.git;a=commit;h=2355f01709eadfd5350c510cdb095b4e3f71f17c)

Fixed by Mark H Weaver, on Fri, 7 Dec 2012.

Avoid signed integer overflow in scm_product

* libguile/numbers.c (scm_product): Avoid signed integer overflow, which modern C compilers are allowed to assume will never happen, thus allowing them to optimize out our overflow checks.

* test-suite/tests/numbers.test (*): Add tests.

~~~
qznc
A nice example for Regehr's post: C and C++ Aren’t Future Proof

<http://blog.regehr.org/archives/880>

~~~
regehr
Awesome, I've added a link to the bottom of the post.

------
TwistedWave
It boils down to:

> (* 4294967296 4294967296)

$1 = 0

My guess is that guile uses the native int type to store small numbers, and
then switches to a bignums, backed by GNU MP when the native ints are too
small. Maybe in that particular case, the check fails, and the computation
overflows the native int.

~~~
alpb
Then why 2^488 works correctly but nothing in 2^(64..487) ?

------
zvrba
This shows that C and C++ are getting more and more out of sync with modern
CPUs. Outdated if you want.

Modern CPUs have wide multiplies (32x32->64), wide divides (64/32->32), a
whole bunch of useful arithmetic status flags, yet C gives you access to
nothing of that. Instead, it forces you to divide first (very slow) before
multiplying to ensure that overflow won't happen.

Even a simple division check before multiplying isn't sufficient, since
there's a border case where the two operands are INT_MIN and -1. In 2nd
complement arithmetic, both division and multiplication will overflow. So you
need yet another check.

But gcc folks are also obtuse: an implementation is allowed to define what is
technically UB. Optimizing out operations that are perfectly defined on a 2nd-
complement architecture seems to be against the "spirit" of C -- that you can
guess the compiler's output with reasonable certainty. I'd call this outright
maliciousness on the part of compiler implementors.

People dislike C++ saying too much is happening behind the curtains. But
examples like this show that C is not much better either.

~~~
colanderman
Dude, you don't know what you're talking about. Compile this for 32-bit x86
(gcc -m32 -O3 -S):

uint64_t foo(uint64_t x, uint64_t y) { return (uint64_t) x * (uint64_t) y; }

Big, ugly, as expected: 3 multiplies and 2 adds. Now compile this:

uint64_t foo(uint32_t x, uint32_t y) { return (uint64_t) x * (uint64_t) y; }

One instruction (mull). GCC totally knows about 32x32 -> 64 multiplies; you
just don't know how to use them.

~~~
zvrba
Dude, YOU have no idea what you're talking about. I'm talking about C, the
language, not what one particular implementation gives you. AFAIK, C11 doesn't
have a 128-bit int type, so how do you get the full result of 64x64 bit
multiply? How do you access the carry flag? The overflow flag? Etc.

~~~
colanderman
The C language doesn't, and has never, said anything about what the assembled
code should look like. If you want performance, you _have to_ use a good
compiler. That's true with any language.

You're right, C11 doesn't have a 128-bit integer type; that should be
remedied. Fortunately all major compilers (GCC, LLVM, VC++, and ICC) provide
one.

Carry: "if (x + y < x)" compiles to code that uses the carry flag.

Overflow: "if ((int32_t) (x + y) < 0)" compiles to code that uses the overflow
flag.

And the best part is, code written like this is portable to architectures such
as TileGX that _don't_ have status flags.

~~~
zvrba
> Carry: "if (x + y < x)" compiles to code that uses the carry flag.

If x and y are signed, you're relying on UB, which the compiler might optimize
out.

> "if ((int32_t) (x + y) < 0)" compiles to code that uses the overflow flag.

Again, which type are x and y? If signed, this code is UB. If unsigned, then
the result of conversion to int32 may not fit into int32, in which case the
result is implementation-defined, or and implementation-defined signal is
raised.

Portable? Right.

------
stuffihavemade
Does the behavior change if you take out the even condition, or if you make
the algorithm tail recursive?

------
deltasquared
Looks to me like an underlying overflow bit isn't being checked properly in
some cases.

------
deltasquared
Looking at guile version 2.0 this behaviour is fixed.

~~~
TwistedWave
I could reproduce it in guile 2.0.6

~~~
deltasquared
What version of C compiler are you using?

