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-09-23 16:41 -------
Ok, Vincent;

Semantics aside, and disregarding all comments to the contrary, here is a small
app which does binary to decimal conversion for all examples we have had to
date.  "Money where the mouth is" sort of stuff.  No doubt it could be pulled
apart in any number of ways, but it substantiates the theory and proves that
what I have been saying is possible.

I have not fully tested this against all cases, but those we have discussed here
all produce mathematically sound conversions.

#include <math.h>
#include <string.h>
#include <stdio.h>

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     = pow10(-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(x) > 0)                                   // recurse while |x| > 0
    tobuf(max, len, buf, x, precision-1, max_prec, carry);
  else {                                             // x == 0 - first digit
    if (*len >= max) return;
    if (sign) { buf[*len] = '-'; *len = *len + 1; }
    if (*len >= max) return;
    if (precision == 0) { buf[*len] = '0'; *len = *len + 1; }
  }
  // for first and subsequent digits, add the digit to the buffer
  if (*len < max && precision==0) { buf[*len] = '.'; *len = *len + 1; }
  if (*len >= max) return;
  buf[*len] = '0' + l_dec;
  *len = *len + 1;
}

int dbl2buf(size_t max, char *buf, double x, int precision) {
  const int DECIMALS=15;
  int    max_dec = DECIMALS-(int)(trunc(log10(fabs(x)))+1); // max significant
digits
  double max_prec = pow10(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

  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
}

int main (void)
{
  int n;
  double x;
  char buf[64];
  x = 5000.525; dbl2buf(sizeof(buf), buf, x, 2); printf("%.15f = %s\n", x, buf);
  x = 2596.625; dbl2buf(sizeof(buf), buf, x, 2); printf("%.15f = %s\n", x, buf);
  x = 2596.525; dbl2buf(sizeof(buf), buf, x, 2); printf("%.15f = %s\n", x, buf);
  for (x = -8.5; x <= 8.5; ++x) {
    dbl2buf(sizeof(buf), buf, x, 0); printf("%.15f = %s\n", x, buf);
  }
  for (x = -8.5; x <= 8.5; ++x) {
    dbl2buf(sizeof(buf), buf, x, 24); printf("%.15f = %s\n", x, buf);
  }

  return 0;
}
_________________________________________
Program output:
5000.524999999999636 = 5000.53
2596.625000000000000 = 2596.63
2596.525000000000091 = 2596.53
-8.500000000000000 = -9
-7.500000000000000 = -8
-6.500000000000000 = -7
-5.500000000000000 = -6
-4.500000000000000 = -5
-3.500000000000000 = -4
-2.500000000000000 = -3
-1.500000000000000 = -2
-0.500000000000000 = -1
0.500000000000000 = 1
1.500000000000000 = 2
2.500000000000000 = 3
3.500000000000000 = 4
4.500000000000000 = 5
5.500000000000000 = 6
6.500000000000000 = 7
7.500000000000000 = 8
8.500000000000000 = 9
-8.500000000000000 = -8.500000000000000000000000
-7.500000000000000 = -7.500000000000000000000000
-6.500000000000000 = -6.500000000000000000000000
-5.500000000000000 = -5.500000000000000000000000
-4.500000000000000 = -4.500000000000000000000000
-3.500000000000000 = -3.500000000000000000000000
-2.500000000000000 = -2.500000000000000000000000
-1.500000000000000 = -1.500000000000000000000000
-0.500000000000000 = -0.500000000000000000000000
0.500000000000000 = 0.500000000000000000000000
1.500000000000000 = 1.500000000000000000000000
2.500000000000000 = 2.500000000000000000000000
3.500000000000000 = 3.500000000000000000000000
4.500000000000000 = 4.500000000000000000000000
5.500000000000000 = 5.500000000000000000000000
6.500000000000000 = 6.500000000000000000000000
7.500000000000000 = 7.500000000000000000000000
8.500000000000000 = 8.500000000000000000000000

Kindest regards,

-- 


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]