The real problem is that you always want to use exactly the same code for
ALL cases of string-to-float conversion.  The first public example of this
problem was the FORTRAN II compiler from IBM in the 60's.  The compiler and
the IO library was written by two different people and so constants in
program code didn't match those read in from IO, OOPS!  you'd think
people would remember and learn from the past.  By exactly the same,
I mean exactly the same machine code (hardware floating point status and rounding mode 
bits included) not just the same HL source code.  I.e., You
need exactly one routine, compiled only once and used EVERYWHERE.  It also pays
to have a single routine for the other direction that has the property:
S = ftos(atof(S)) and F = atof(ftoa(F)).  Otherwise you get obscure
very hard to find bugs.

--
Mark Biggar
[EMAIL PROTECTED]
> 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