5.000...007.to_s => "5"

Discussion in 'Ruby' started by Gavin Kistner, Apr 21, 2004.

  1. irb(main):001:0> 5.00000000000007.to_s
    => "5.00000000000007"

    irb(main):002:0> 5.000000000000007.to_s
    => "5.00000000000001"

    irb(main):003:0> 5.0000000000000007.to_s
    => "5"

    irb(main):004:0> 5.00000000000000007.to_s
    => "5.0"

    Note the weirdness in the third item, as reported by 'quix' on the
    #ruby-lang IRC channel.

    (The issue is not the lack of float precision...that's gotta give out
    at some point. The issue is the "5" vs. "5.0" discrepancy.)

    --
    (-, /\ \/ / /\/
     
    Gavin Kistner, Apr 21, 2004
    #1
    1. Advertising

  2. On Thu, Apr 22, 2004 at 02:48:18AM +0900, Gavin Kistner wrote:
    > irb(main):001:0> 5.00000000000007.to_s
    > => "5.00000000000007"
    >
    > irb(main):002:0> 5.000000000000007.to_s
    > => "5.00000000000001"
    >
    > irb(main):003:0> 5.0000000000000007.to_s
    > => "5"
    >
    > irb(main):004:0> 5.00000000000000007.to_s
    > => "5.0"
    >
    > Note the weirdness in the third item, as reported by 'quix' on the
    > #ruby-lang IRC channel.
    >
    > (The issue is not the lack of float precision...that's gotta give out
    > at some point. The issue is the "5" vs. "5.0" discrepancy.)


    Try this patch:

    --- numeric.c.orig 2004-04-21 04:31:04.000000000 +0300
    +++ numeric.c 2004-04-21 04:31:08.000000000 +0300
    @@ -490,10 +490,10 @@
    VALUE flt;
    {
    char buf[32];
    - char *fmt = "%.15g";
    + char *fmt = "%.15f";
    double value = RFLOAT(flt)->value;
    double avalue, d1, d2;
    -
    +
    if (isinf(value))
    return rb_str_new2(value < 0 ? "-Infinity" : "Infinity");
    else if(isnan(value))


    --
    University of Athens I bet the human brain
    Physics Department is a kludge --Marvin Minsky
     
    Elias Athanasopoulos, Apr 21, 2004
    #2
    1. Advertising

  3. Gavin Kistner

    Guest

    Hi,

    At Thu, 22 Apr 2004 02:48:18 +0900,
    Gavin Kistner wrote in [ruby-talk:97891]:
    > (The issue is not the lack of float precision...that's gotta give out
    > at some point. The issue is the "5" vs. "5.0" discrepancy.)


    Seems the issue about the boundary between "e" and "f".


    Index: numeric.c
    ===================================================================
    RCS file: /cvs/ruby/src/ruby/numeric.c,v
    retrieving revision 1.106
    diff -u -2 -p -r1.106 numeric.c
    --- numeric.c 14 Apr 2004 04:06:25 -0000 1.106
    +++ numeric.c 21 Apr 2004 18:54:14 -0000
    @@ -517,5 +517,5 @@ flo_to_s(flt)
    else fmt = "%.16e";
    }
    - else if ((d1 = modf(value, &d2)) == 0) {
    + else if (fabs(modf(avalue, &d2)) <= 1.0e-15) {
    fmt = "%.1f";
    }


    --
    Nobu Nakada
     
    , Apr 21, 2004
    #3
  4. On Thu, Apr 22, 2004 at 03:59:54AM +0900, wrote:
    > Hi,
    >
    > At Thu, 22 Apr 2004 02:48:18 +0900,
    > Gavin Kistner wrote in [ruby-talk:97891]:
    > > (The issue is not the lack of float precision...that's gotta give out
    > > at some point. The issue is the "5" vs. "5.0" discrepancy.)

    >
    > Seems the issue about the boundary between "e" and "f".


    Still, default fmt in flo_to_s() is iniatialized to 'g' (which
    cuts trailing decimal points).

    Regards,
    --
    University of Athens I bet the human brain
    Physics Department is a kludge --Marvin Minsky
     
    Elias Athanasopoulos, Apr 21, 2004
    #4
  5. Hi,

    In message "Re: 5.000...007.to_s => "5""
    on 04/04/22, Elias Athanasopoulos <> writes:

    |> Seems the issue about the boundary between "e" and "f".
    |
    |Still, default fmt in flo_to_s() is iniatialized to 'g' (which
    |cuts trailing decimal points).

    It needs to be 'g' for scientific notation for some very big (or very
    small) numbers. By the way, 1.0e-15 epsilon is not sufficient for
    numbers such as 5.0000000000000046. I propose 5.0e-15.

    matz.
     
    Yukihiro Matsumoto, Apr 21, 2004
    #5
  6. Gavin Kistner

    Guest

    Hi,

    At Thu, 22 Apr 2004 04:36:05 +0900,
    Jeff Mitchell wrote in [ruby-talk:97924]:
    > This fixes 1.000..7 but does not fix two other cases I didn't mention,
    >
    > arda:~/cur/ruby> ./ruby -e 'puts 1.00000000000004.to_s'
    > 1.00000000000004
    > arda:~/cur/ruby> ./ruby -e 'puts 1.000000000000004.to_s'
    > 1
    > arda:~/cur/ruby> ./ruby -e 'puts 1.0000000000000004.to_s'
    > 1.0
    > arda:~/cur/ruby> ./ruby -e 'puts 1.99999999999999.to_s'
    > 1.99999999999999
    > arda:~/cur/ruby> ./ruby -e 'puts 1.999999999999999.to_s'
    > 2
    > arda:~/cur/ruby> ./ruby -e 'puts 1.9999999999999999.to_s'
    > 2.0


    Hmmm..., I didn't consider about upper boundary.


    Index: numeric.c
    ===================================================================
    RCS file: /cvs/ruby/src/ruby/numeric.c,v
    retrieving revision 1.106
    diff -u -2 -p -r1.106 numeric.c
    --- numeric.c 14 Apr 2004 04:06:25 -0000 1.106
    +++ numeric.c 22 Apr 2004 01:00:45 -0000
    @@ -517,5 +517,5 @@ flo_to_s(flt)
    else fmt = "%.16e";
    }
    - else if ((d1 = modf(value, &d2)) == 0) {
    + else if ((d1 = modf(avalue, &d2)) <= 5.0e-15 || (1.0 - d1) <= 5.0e-15) {
    fmt = "%.1f";
    }


    --
    Nobu Nakada
     
    , Apr 22, 2004
    #6
  7. Gavin Kistner

    Guest

    Hi,

    At Thu, 22 Apr 2004 11:33:44 +0900,
    Jeff Mitchell wrote in [ruby-talk:97988]:
    > Oddly enough, with your patch the first two floats are fixed,
    > showing 24.0 and 28.0 (which is entirely a coincidence; this
    > list didn't exist when I came up with the original numbers).
    > The rest are as shown.


    $ cat f.rb
    #! /usr/bin/ruby
    require 'hexfloat' # http://nokada.jin.gr.jp/ruby/hexfloat.rb
    require 'stringio'

    def show(a)
    d = a%1
    printf "%s %-20s %-20s %-20s %-20s\n", a.class, a.to_s, a.hex, d.hex, (1.0-d).hex
    end

    [5.00000000000007, 5.000000000000007, 5.0000000000000007, 5.00000000000000007,
    1.00000000000004, 1.000000000000004, 1.0000000000000004,
    1.99999999999999, 1.999999999999999, 1.9999999999999999].each(&method:)show))

    StringIo_Open(*DATA.read.unpack("m")) do |file|
    begin
    while a = Marshal.load(file)
    show(a)
    end
    rescue EOFError
    end
    end

    __END__
    BAhmGjI0LjAwMDAwMDAwMDAwMDAwNAAAAQQIZhoyNy45OTk5OTk5OTk5OTk5OTYA//8ECGYaNDQu
    MDAwMDAwMDAwMDAwMDA3AAABBAhmGjQ1Ljk5OTk5OTk5OTk5OTk5MwD//wQIZho0OC4wMDAwMDAw
    MDAwMDAwMDcAAAEECGYaNTEuOTk5OTk5OTk5OTk5OTkzAP//BAhmGjU0LjAwMDAwMDAwMDAwMDAw
    NwAAAQQIZho4My45OTk5OTk5OTk5OTk5ODYA//8=

    $ ./ruby f.rb
    Float 5.00000000000007 0x5.000000000013c 0x1.3cp-44 0xf.fffffffffec4p-4
    Float 5.00000000000001 0x5.000000000002 0x2.0p-48 0xf.ffffffffffep-4
    Float 5.0 0x5.0000000000004 0x4.0p-52 0xf.fffffffffffcp-4
    Float 5.0 0x5.0 0x0.0p-4 0x1.0
    Float 1.00000000000004 0x1.00000000000b4 0xb.4p-48 0xf.ffffffffff4cp-4
    Float 1.0 0x1.0000000000012 0x1.2p-48 0xf.ffffffffffeep-4
    Float 1.0 0x1.0000000000002 0x2.0p-52 0xf.fffffffffffep-4
    Float 1.99999999999999 0x1.fffffffffffd3 0xf.ffffffffffd3p-4 0x2.dp-48
    Float 2.0 0x1.ffffffffffffb 0xf.fffffffffffbp-4 0x5.0p-52
    Float 2.0 0x2.0 0x0.0p-4 0x1.0
    Float 24.0 0x1.8000000000001p+4 0x1.0p-48 0xf.fffffffffffp-4
    Float 28.0 0x1.bffffffffffffp+4 0xf.fffffffffffp-4 0x1.0p-48
    Float 44.0 0x2.c000000000002p+4 0x2.0p-48 0xf.ffffffffffep-4
    Float 46.0 0x2.dfffffffffffep+4 0xf.ffffffffffep-4 0x2.0p-48
    Float 48.0 0x3.0000000000002p+4 0x2.0p-48 0xf.ffffffffffep-4
    Float 52.0 0x3.3fffffffffffep+4 0xf.ffffffffffep-4 0x2.0p-48
    Float 54.0 0x3.6000000000002p+4 0x2.0p-48 0xf.ffffffffffep-4
    Float 84.0 0x5.3fffffffffffcp+4 0xf.ffffffffffcp-4 0x4.0p-48

    The range to be .f format is affected by also its precision.
    This might be rather realistic.


    Index: numeric.c
    ===================================================================
    RCS file: /cvs/ruby/src/ruby/numeric.c,v
    retrieving revision 1.106
    diff -u -2 -p -r1.106 numeric.c
    --- numeric.c 14 Apr 2004 04:06:25 -0000 1.106
    +++ numeric.c 22 Apr 2004 04:34:22 -0000
    @@ -517,8 +517,6 @@ flo_to_s(flt)
    else fmt = "%.16e";
    }
    - else if ((d1 = modf(value, &d2)) == 0) {
    - fmt = "%.1f";
    - }
    sprintf(buf, fmt, value);
    + if (!strchr(buf, '.')) strcat(buf, ".0");

    return rb_str_new2(buf);


    --
    Nobu Nakada
     
    , Apr 22, 2004
    #7
  8. nice things, was [Re: 5.000...007.to_s => "5"]

    wrote:
    >
    > $ cat f.rb

    [snip]

    This is nice.. I wasn't aware that Ruby has a '__END__' tag.

    server> ruby d.rb
    before
    middle
    after
    server> expand -2 d.rb
    puts "before"
    eval DATA.read
    puts "after"
    __END__
    puts "middle"
    server>

    Are there other sweet things like this?

    --
    Simon Strandgaard
     
    Simon Strandgaard, Apr 22, 2004
    #8
  9. Re: nice things, was [Re: 5.000...007.to_s => "5"]

    "Simon Strandgaard" <> schrieb im Newsbeitrag
    news:...
    > wrote:
    > >
    > > $ cat f.rb

    > [snip]
    >
    > This is nice.. I wasn't aware that Ruby has a '__END__' tag.
    >
    > server> ruby d.rb
    > before
    > middle
    > after
    > server> expand -2 d.rb
    > puts "before"
    > eval DATA.read
    > puts "after"
    > __END__
    > puts "middle"
    > server>
    >
    > Are there other sweet things like this?


    lotsof
    ;-)

    robert
     
    Robert Klemme, Apr 22, 2004
    #9
  10. Gavin Kistner

    H.Yamamoto Guest

    Sorry for late posting. Does this patch solve your problem?

    * numeric.c(flo_to_s): use DBL_DIG to show only significant digits.

    Index: numeric.c
    ===================================================================
    RCS file: /ruby/ruby/numeric.c,v
    retrieving revision 1.107
    diff -u -w -b -p -r1.107 numeric.c
    --- numeric.c 7 May 2004 08:44:15 -0000 1.107
    +++ numeric.c 10 May 2004 04:00:45 -0000
    @@ -489,10 +489,9 @@ static VALUE
    flo_to_s(flt)
    VALUE flt;
    {
    - char buf[32];
    - char *fmt = "%.15f";
    + char fmt[sizeof(int)*3+10], buf[sizeof(double)*3+sizeof(int)*3+20];
    double value = RFLOAT(flt)->value;
    - double avalue, d1, d2;
    + int exp;
    char *p, *e;

    if (isinf(value))
    @@ -500,13 +499,25 @@ flo_to_s(flt)
    else if(isnan(value))
    return rb_str_new2("NaN");

    - avalue = fabs(value);
    - if (avalue < 1.0e-7 || avalue >= 1.0e15) {
    - fmt = "%.16e";
    - }
    + sprintf(fmt, "%%.%de", DBL_DIG - 1);
    sprintf(buf, fmt, value);
    - if (!(e = strchr(buf, 'e'))) {
    - e = buf + strlen(buf);
    + e = strchr(buf, 'e');
    + exp = atoi(e+1);
    + if (-7 <= exp && exp < DBL_DIG - 1) {
    + char *d = strchr(buf, '.');
    + if (exp > 0) {
    + memmove(d, d+1, exp);
    + d[exp] = '.';
    + }
    + if (exp < 0) { /* needs room in `buf' for shifting (currently 7) */
    + int n = -exp;
    + d[0] = d[-1];
    + memmove(d+n, d, strlen(d)+1);
    + e += n;
    + memset(buf, '0', n+1);
    + buf[1] = '.';
    + }
    + *e = '\0';
    }
    p = e;
    while (*--p=='0')
     
    H.Yamamoto, May 10, 2004
    #10
  11. Gavin Kistner

    H.Yamamoto Guest

    This thread seems dead though...

    There was bug in my patch. Correct one is posted at [ruby-dev:23507]

    > Float(-0.1).to_s #=> "0.11" (should be "-0.1")


    Thank you.
     
    H.Yamamoto, May 14, 2004
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. rote
    Replies:
    3
    Views:
    433
    Mark Rae [MVP]
    Jan 24, 2008
  2. lector

    storing 1,000,000 records

    lector, Apr 6, 2008, in forum: C Programming
    Replies:
    19
    Views:
    477
    Barry Schwarz
    Apr 8, 2008
  3. Replies:
    1
    Views:
    447
  4. Replies:
    0
    Views:
    591
  5. bad_knee

    regex to convert 1000000 -> 1,000,000 ?

    bad_knee, Nov 14, 2003, in forum: Perl Misc
    Replies:
    39
    Views:
    333
    Tad McClellan
    Nov 19, 2003
Loading...

Share This Page