Its the old problem of rounding errors in floating point arithmetic.

In string_to_num() in src/string.c,
       f = f * sign * pow(10.0, exponent);     /* ugly, oh yeah */

Which in this case translates to 12.0*-1*0.1 which is -1.2000...2 != -1.2.

I replaced this bit of code from a block I found in mysql. I only tested this
on linux, but it seems to do the trick. See attached.


Leopold Toetsch wrote:

Simon Glover <[EMAIL PROTECTED]> wrote:



This code:





new P0, .PerlNum
set P0, -1.2
new P1, .PerlString
set P1, "-1.2"
eq_num P0, P1, OK
print "not "
OK: print "ok\n"
end





[And yes, I'm well aware of the problems inherent in doing floating point
comparisons.



Breakpoint 1, Parrot_PerlNum_cmp_num (interpreter=0x82654f0, pmc=0x40305850, value=0x40305838) at perlnum.c:301 301 diff = PMC_num_val(pmc) - VTABLE_get_number(interpreter, value); (gdb) n 302 return diff > 0 ? 1 : diff < 0 ? -1 : 0; (gdb) p diff $1 = 2.2204460492503131e-16 (gdb)



Simon



leo



*** tmp/parrot/src/string.c     Sat Mar  6 03:00:06 2004
--- parrot/src/string.c Wed Mar 17 12:24:02 2004
***************
*** 1836,1844 ****
          int exp_sign = 0;
          INTVAL in_exp = 0;
          INTVAL in_number = 0;
!         FLOATVAL exponent = 0;
          INTVAL fake_exponent = 0;
          INTVAL digit_family = 0;
  
          while (start < end) {
              UINTVAL c = s->encoding->decode(start);
--- 1836,1845 ----
          int exp_sign = 0;
          INTVAL in_exp = 0;
          INTVAL in_number = 0;
!         INTVAL exponent = 0;
          INTVAL fake_exponent = 0;
          INTVAL digit_family = 0;
+         FLOATVAL exp_log=10.0, exp_val=1.0;
  
          while (start < end) {
              UINTVAL c = s->encoding->decode(start);
***************
*** 1849,1855 ****
  
              if (df && df == digit_family) {
                  if (in_exp) {
!                     exponent = exponent * 10 + s->type->get_digit(s->type,c);
                      if (!exp_sign) {
                          exp_sign = 1;
                      }
--- 1850,1856 ----
  
              if (df && df == digit_family) {
                  if (in_exp) {
!                     exponent = exponent + s->type->get_digit(s->type,c);
                      if (!exp_sign) {
                          exp_sign = 1;
                      }
***************
*** 1906,1912 ****
  
          exponent = fake_exponent + exponent * exp_sign;
  
!         f = f * sign * pow(10.0, exponent);     /* ugly, oh yeah */
      }
  
      return f;
--- 1907,1936 ----
  
          exponent = fake_exponent + exponent * exp_sign;
  
!         if(exponent < 0) {
!             exponent = -exponent; 
!             exp_sign=-1;
!         }
! 
!         for (;;) {
!             if (exponent & 1) {
!                 exp_val *= exp_log;
!                 exponent--;
!             }
!             if (!exponent)
!                 break;
!             exp_log *= exp_log;
!             exponent >>= 1;
!         }
!         
!         if(exp_sign < 0)
!             f /= exp_val;
!         else
!             f *= exp_val;
! 
!         
!         if(sign < 0)
!             f = -f;
      }
  
      return f;

Reply via email to