This is the mail archive of the glibc-bugs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug libc/4943] Inconsistent rounding behaviour for sprintf and IEEE doubles


------- Additional Comments From paul at inet dot co dot za  2007-10-03 10:03 -------
Hi, Vincent

I have, as you suggested performed some extensive testing against my binary to
decimal conversion procedure.  There have been a few changes, mostly cosmetic,
but one specific bug fix where the recursion terminated prematurely with a zero
value and existing carry.

Please consider the following code:
________________________________________________________________________
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

//______________________________________________________________________
// Utility function converts an IEEE double precision number to a
// fixed precision decimal format stored in a buffer.
void tobuf(size_t max, int *len, char *buf, 
           double x, int precision, double max_prec, double carry)
{
  int    sign  = x < 0;                              // remember the sign
  double q     = pow(10,-precision);                 // current mask
  double y     = x==0?0:fmod(fabs(x), q);            // modulus
  double l_div = round(y*max_prec)/max_prec+carry;   // significant digit
  int    l_dec = (int)round(l_div*10/q);             // round to decimal
  
  carry = l_dec>=10?l_div:0;                         // carry forward?
  l_dec = l_dec % 10;                                // this decimal
  x = x>0?x-y:x+y;                                   // subtract modulus
  
  if (fabs(carry) > 0 || fabs(x) > 0)                // recurse while |x| > 0
    tobuf(max, len, buf, x, precision-1, max_prec, carry);
  else {                                             // x == 0 - first digit
    if (*len+1 < max && sign) buf[(*len)++] = '-';
    if (*len+2 < max && precision >= 0) { 
      buf[(*len)++] = '0'; 
      buf[(*len)++] = '.'; 
    }
    while (*len+1 < max && precision-- > 0) 
      buf[(*len)++] = '0';
  }
  if (*len+1 < max && precision == 0)
    buf[(*len)++] = '.'; 
  
  // for first and subsequent digits, add the digit to the buffer
  if (*len+1 >= max) return;
  if (l_dec < 0) l_dec = 0;
  buf[(*len)++] = '0' + l_dec;
}


//______________________________________________________________________
// Convert the value x to a decimal representation stored in a buffer
int dbl2buf(size_t max, char *buf, double x, int precision) {
  const int DECIMALS=15;                               // max significant digits
  int    max_dec = DECIMALS-(int)(trunc(log10(fabs(x)))+1); 
  double max_prec = pow(10,max_dec);             // magnitude for precision loss
  int    len = 0;                                      // buffer length init
  
  double y       = x==0?0:fmod(fabs(x), 1/max_prec);  // determine error
  double l_carry = round(y*max_prec)/max_prec;     // error is carried forward 
  x = x>0?x-y:x+y;                                    // subtract modulus

  if (x != x) { strncpy(buf, "NAN", max); return 0; }
  if ((x-x) != (x-x)) { strncpy(buf, "INF", max); return 0; }
  
  tobuf(max, &len, buf, x, precision-1, max_prec, l_carry); // fill in buffer
  buf[len] = 0;                                             // terminate buffer
  return len;                                      // return buffer length used
}

//______________________________________________________________________
//  Test the dbl2buf function.
int main (void)
{
  int n, nfails=0, nspfails=0;
  double x;
  char buf1[64];
  char buf2[64];
  char buf3[64];
  
  for(n=0;n<10000000;n++){
    x = random() / (double)random();
    dbl2buf(sizeof(buf1), buf1, x, 15);
    
    x = atof(buf1);  // initialise test value, precision 15.
    
    dbl2buf(sizeof(buf2), buf2, x, 15);
    snprintf(buf3, sizeof(buf3), "%.15f", x);
    if (strcmp(buf1, buf2)) {
      nfails++;
    }
    else if (strcmp(buf2, buf3)) {
      nspfails++;
    }    
  }
  printf("%i random floating point values tested\n", n);
  printf("  Number of dbl2buf failures: %i\n", nfails);
  printf("  Number of sprintf failures: %i\n", nspfails);
  return 0;
}


Results:
10000000 random floating point values tested
  Number of dbl2buf failures: 0
  Number of sprintf failures: 394383
____________________________________________________________________________

Please be aware that this bug report is now linked from an article published
this month (October) in the Linux Gazette.

Kind regards,
Paul Sephton

-- 


http://sourceware.org/bugzilla/show_bug.cgi?id=4943

------- You are receiving this mail because: -------
You are on the CC list for the bug, or are watching someone who is.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]