[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

computers + floating-point = a chore

Yossef Mendelssohn

10/22/2007 8:28:00 PM

I recall seeing a few posts about the "fun" involving floating-point
representation of numbers, but this is something I found a little
surprising:

Cassady:~ yossef$ irb --prompt simple
>> require 'bigdecimal'
=> true
>> bd = BigDecimal('19.76')
=> #<BigDecimal:9ccc,'0.1976E2',8(8)>
>> bd <=> 19.76
=> -1
>> bd.to_f <=> 19.76
=> -1
>> bd.to_f
=> 19.76
>> 19.76 <=> 19.76
=> 0

Odd, isn't it? What's going on here?

>> 19.75999999999999999999999999999999999
=> 19.76

Ah, maybe this is it.

>> 19.75999999999999999999999999999999999 <=> 19.76
=> 0

Nope!
Well, a bit of experimentation led me to this, which demonstrates two
threshold points.

>> 19.7599999999999
=> 19.7599999999999
>> 19.75999999999999
=> 19.76
>> 19.75999999999999 <=> 19.76
=> -1
>> 19.759999999999999 <=> 19.76
=> -1
>> 19.7599999999999999 <=> 19.76
=> 0

It appears 19.75999999999999 (that's 12 9s, or 14 decimal places
total) is just close enough to *display* rounded up, but not actually
*become* that number. Adding two more 9s makes it close enough to
actually become that number.

No question or call for help, really. I just wanted to point out
something that intrigued me.

--
-yossef


4 Answers

John Joyce

10/22/2007 11:42:00 PM

0

floating point math is always like this.
Any language or framework that purports to have no trouble with it is
actually using objects (or structs) consisting of integers for each
of its aspects. All of the aspects are assembled.

Yossef Mendelssohn

10/23/2007 2:31:00 AM

0

On Oct 22, 6:42 pm, John Joyce <dangerwillrobinsondan...@gmail.com>
wrote:
> floating point math is always like this.
> Any language or framework that purports to have no trouble with it is
> actually using objects (or structs) consisting of integers for each
> of its aspects. All of the aspects are assembled.

Yes, I know the troubles with floating point. I think what I found
most interesting was a number that claimed to be 19.76 when it
actually wasn't, simply because it's more convenient (?) to display it
as such.

--
-yossef


Gary Wright

10/23/2007 3:15:00 AM

0


On Oct 22, 2007, at 10:30 PM, Yossef Mendelssohn wrote:
> Yes, I know the troubles with floating point. I think what I found
> most interesting was a number that claimed to be 19.76 when it
> actually wasn't, simply because it's more convenient (?) to display it
> as such.

Well, the culprit is actually Float#to_s, which has it's own idea of
how to convert floating point numbers to text. In particular it
defaults
to 15 digits of precession and also works hard to strip trailing zero's:

$ irb
irb(main):001:0> a = 19.76
=> 19.76
irb(main):002:0> a.to_s
=> "19.76"
irb(main):003:0> sprintf "%#.15g", a
=> "19.7600000000000"
irb(main):004:0> sprintf "%#.32g", a
=> "19.760000000000001563194018672220"
irb(main):005:0>


You can see from this session that 15 digits of precision is not
enough to 'show' that a isn't really 19.76.

What was surprising to me is that Float#to_s doesn't just use a standard
sprintf format string:

static VALUE
flo_to_s(flt)
VALUE flt;
{
char buf[32];
double value = RFLOAT(flt)->value;
char *p, *e;

if (isinf(value))
return rb_str_new2(value < 0 ? "-Infinity" : "Infinity");
else if(isnan(value))
return rb_str_new2("NaN");

sprintf(buf, "%#.15g", value); /* ensure to print decimal point */
if (!(e = strchr(buf, 'e'))) {
e = buf + strlen(buf);
}
if (!ISDIGIT(e[-1])) { /* reformat if ended with decimal point
(ex 111111111111111.) */
sprintf(buf, "%#.14e", value);
if (!(e = strchr(buf, 'e'))) {
e = buf + strlen(buf);
}
}
p = e;
while (p[-1]=='0' && ISDIGIT(p[-2]))
p--;
memmove(p, e, strlen(e)+1);
return rb_str_new2(buf);
}


Gary Wright

Yossef Mendelssohn

10/23/2007 1:33:00 PM

0

On Oct 22, 10:14 pm, Gary Wright <gwtm...@mac.com> wrote:
> On Oct 22, 2007, at 10:30 PM, Yossef Mendelssohn wrote:
>
> > Yes, I know the troubles with floating point. I think what I found
> > most interesting was a number that claimed to be 19.76 when it
> > actually wasn't, simply because it's more convenient (?) to display it
> > as such.
>
> Well, the culprit is actually Float#to_s, which has it's own idea of
> how to convert floating point numbers to text. In particular it
> defaults
> to 15 digits of precession and also works hard to strip trailing zero's:
>
> $ irb
> irb(main):001:0> a = 19.76
> => 19.76
> irb(main):002:0> a.to_s
> => "19.76"
> irb(main):003:0> sprintf "%#.15g", a
> => "19.7600000000000"
> irb(main):004:0> sprintf "%#.32g", a
> => "19.760000000000001563194018672220"
> irb(main):005:0>
>
> You can see from this session that 15 digits of precision is not
> enough to 'show' that a isn't really 19.76.
>
> What was surprising to me is that Float#to_s doesn't just use a standard
> sprintf format string:
>
> static VALUE
> flo_to_s(flt)
> VALUE flt;
> {
> char buf[32];
> double value = RFLOAT(flt)->value;
> char *p, *e;
>
> if (isinf(value))
> return rb_str_new2(value < 0 ? "-Infinity" : "Infinity");
> else if(isnan(value))
> return rb_str_new2("NaN");
>
> sprintf(buf, "%#.15g", value); /* ensure to print decimal point */
> if (!(e = strchr(buf, 'e'))) {
> e = buf + strlen(buf);
> }
> if (!ISDIGIT(e[-1])) { /* reformat if ended with decimal point
> (ex 111111111111111.) */
> sprintf(buf, "%#.14e", value);
> if (!(e = strchr(buf, 'e'))) {
> e = buf + strlen(buf);
> }
> }
> p = e;
> while (p[-1]=='0' && ISDIGIT(p[-2]))
> p--;
> memmove(p, e, strlen(e)+1);
> return rb_str_new2(buf);
>
> }
>
> Gary Wright

Thanks for this, Gary. I wasn't expecting that Float#inspect would
call #to_s, and obviously didn't even bother to test anything out with
sprintf.

This also explains another Float niggle, which is why it will display
"Infinity" but trying to use Infinity as a constant doesn't work.

Maybe I should spend more time looking at the Ruby source.

--
-yossef