Subject: bug#13031: large numbers

From: Jozef Chraplewski <[email-masked]>
Date: Fri, 7 Dec 2012 11:40:16 +0100

Hey Mark,

Yes, I use 64-bit machine and (* (expt 2 32) (expt 2 32)) produces 0 (without the patch).

I've applied your fix and it works perfectly.
I suppose that you will add the patch to the next stable version (2.0.8 ??)

Thanks for help and great work!

Best,
Jozef

On Dec 5, 2012, at 5:20 PM, Mark H Weaver <[email-masked]> wrote:

> I wrote:
>> I guess that on your system, (* 65536 65536) evaluates to 0.
>> Is that right?
> 
> I should have mentioned that if you're on a 64-bit system, then it may
> instead be the case that (* (expt 2 32) (expt 2 32)) evaluates to 0.
> Same bug either way, and the rest of my previous email still applies.
> 
>    Thanks,
>      Mark
> 
> 
>> If so, I believe the problem is caused by an aggressive optimization in
>> recent versions of Clang, which breaks Guile's logic for detecting
>> overflow when multiplying two fixnums.
>> 
>> Currently, Guile computes kk = xx * yy and checks for overflow by
>> verifying that kk / xx == yy.
>> 
>> I believe that Clang is optimizing out the check, because recent C
>> standards permit C implementations to assume that signed integer
>> arithmetic will never overflow.  For details, see:
>> http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
>> 
>> One solution is to compile with the "-fwrapv" option, which should
>> disable the optimization.
>> 
>> Another solution is to apply the following patch.
>> 
>> Jozef, would you be willing to test this patch and tell me if it fixes
>> the problem?
>> 
>>   Many thanks,
>>      Mark
>> 
>> 
>> 
>> diff --git a/libguile/numbers.c b/libguile/numbers.c
>> index 52e227f..66c95db 100644
>> --- a/libguile/numbers.c
>> +++ b/libguile/numbers.c
>> @@ -7640,10 +7640,16 @@ scm_product (SCM x, SCM y)
>>       if (SCM_LIKELY (SCM_I_INUMP (y)))
>> 	{
>> 	  scm_t_inum yy = SCM_I_INUM (y);
>> -	  scm_t_inum kk = xx * yy;
>> -	  SCM k = SCM_I_MAKINUM (kk);
>> -	  if ((kk == SCM_I_INUM (k)) && (kk / xx == yy))
>> -	    return k;
>> +#if SCM_I_FIXNUM_BIT < 32 && SCM_HAVE_T_INT64
>> +          scm_t_int64 kk = xx * (scm_t_int64) yy;
>> +          if (SCM_FIXABLE (kk))
>> +            return SCM_I_MAKINUM (kk);
>> +#else
>> +          scm_t_inum axx = (xx > 0) ? xx : -xx;
>> +          scm_t_inum ayy = (yy > 0) ? yy : -yy;
>> +          if (SCM_MOST_POSITIVE_FIXNUM / axx >= ayy)
>> +            return SCM_I_MAKINUM (xx * yy);
>> +#endif
>> 	  else
>> 	    {
>> 	      SCM result = scm_i_inum2big (xx);