I don't think there is any bug here. This is what I've seen from a
little digging:

1) cygwin strtod rounds to even, with about DECIMAL_DIG (==21) digits
precision, as recommended by 7.20.1.3 of WG14/N843. (It acts strange
when the rounding mode is not round to nearest, but since newlib
doesn't provide fesetround(), that's your own problem if you insist on
changing rounding mode by hand, as I understand it).

2) cygwin gcc rounds with *lots* more precision than DECIMAL_DIG,
about 50 digits. It rounds towards zero. These behaviours are a little
different than the recommendation in 6.4.4.2 Ibid., which says
constants should be converted as if by strtod() at runtime, but its
not a big difference.

3) cygwin printf correctly rounds to even, even out to about 41 digits.

4) I have no idea what mingw is doing, but it's different to the
above. Gcc constructs the same double precision constants as on cygwin
but strtod() is different and seems to have less precision, and
printf() seems to work with about 16 digits precision. At a glance I'd
say it rounds to zero, with exactly the precision of

On 05/07/05, Dave Korn wrote:
>  Stepping through the code at the weekend, I followed the 0.105 case as far
> down as ldtoa_r, where I observed that although there was code that would
> round 0.105 up to 0.11 if asked for only two sig.figs, the loop that
> generates successive digits was coming up with the sequence
> '0.10499999999.....', and because the rounding only looks at one digit
> beyond the requested s.f., it 'correctly' rounds 0.104 down to 0.10.

The closest double to 0.105 is actually
0.10499999999999999611421941381195210851728916168212890625, and this
is what printf() is (correctly) handed, and what it (correctly) rounds
to 0.10.
 
>  I say 'correctly' in quotes, because it's the correct way to round 0.104,
> but it's not in general correct to round a number by truncating it and then
> rounding the truncated version.

But I don't think that "truncating it and then rounding the truncated
version" is what printf() does. If we give it 0.125 and 0.375, which
are both representible exactly, then it rounds to 0.12 and 0.38, ie
one goes up and one goes down, to round to even. You can see it's not
simply truncating if you give it 0.1250000000000001, which gets
rounded to 0.13 not 0.12 as it would for truncation.

To summarize: cygwin does the right thing
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>

#ifdef __MINGW32__
#include <fenv.h>
#endif


// show the hex and decimal interpretations of the double
union U{double d;unsigned char c[8];};
void display(union U x)
{
  for(int b=7;b>=0;b--)printf("%02x",x.c[b]);
  printf(" %.21f",x.d);
}


int main(int argc, const char**argv)
{

#ifdef __MINGW32__
  if (argc >= 2)
    {
    int nrm, n;
        n = sscanf (argv[1], "%i", &nrm);
        if (n == 1) switch (nrm)
        {
            case 0:
            case 0x400:
            case 0x800:
            case 0xc00:
                fesetround (nrm);
        }
    }

  printf ("Rmode $%08x\n", fegetround ());
#endif

  printf("DECIMAL_DIG=%d\n",DECIMAL_DIG);

#define PASTE(x) {.s=#x,.d1=strtod(#x,0),.d2=x}
  struct S {const char*s;union U d1,d2;}q[]=
    {
      // test the rounding with the last digits...
      // first right on the boundary, to test round-to-even
      PASTE(1.0000000000000000000000000000000000000000000000000000000), // 1
      PASTE(1.0000000000000001110223024625156540423631668090820312500), // 1 + 
2^-53
      PASTE(1.0000000000000002220446049250313080847263336181640625000), // 1 + 
2^-52
      PASTE(1.0000000000000003330669073875469621270895004272460937500), // 1 + 
2^-52 +2^-53
      {0},
      // now just above the boundary
      PASTE(1.00000000000000000001),
      PASTE(1.00000000000000011103),
      PASTE(1.00000000000000022205),
      PASTE(1.00000000000000033307),
      {0},
      // now just below the boundary
      PASTE(0.99999999999999999999),
      PASTE(1.00000000000000011101),
      PASTE(1.00000000000000022204),
      PASTE(1.00000000000000033301),
      {0},
      PASTE(0.105),
    };
 
  printf("                 1.23456789012345678901234567890\n"); // for counting 
sig figs
  for(int a=0;a<sizeof(q)/sizeof(struct S);a++)
    {
      if(!q[a].s){printf("\n");continue;}
      display(q[a].d1);
      printf(" ");
      display(q[a].d2);
      printf("\n");
    }
  
  printf("\n");

  union U w[]={0.105,0.115,0.125,0.135,0.145,0.155,0.375};
  for(int a=0;a<sizeof(w)/sizeof(union U);a++)
    {
      display(w[a]);
      printf("\n");
    }

  printf("%.40f\n",0.105);

  // here's some numbers with an exact representation in double
  printf("%.20f %.2f\n",.125,.125); 
  printf("%.20f %.2f\n",.375,.375); 
  printf("%.20f %.13f\n",0.95367431640625000000,0.95367431640625000000);
  printf("%.20f %.17f\n",3.5096855163574218750,3.5096855163574218750); // 1 + 
657899/2^18
  printf("%.20f %.15f\n",-1.5035324096679687500,-1.5035324096679687500);

  // how many digits precision does gcc use when making double constants?
  display((union U){.d=1.000000000000000333066907387546962127089500427246});
}

// Useful Mathematica stuff...
/*
  Table[{x/1000.,
            BaseForm[FromDigits[RealDigits[#2, 2] // First // Rest, 2], 16],
            N[#2*2^(-53 + #[[2]]),
              41]} &[#,
        Ceiling[FromDigits[#[[1]], 2]/
            2]] &[RealDigits[x/1000, 2, 54]], {x, 105, 155, 10}]

*/
--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/

Reply via email to