This is the mail archive of the
glibc-bugs@sourceware.org
mailing list for the glibc project.
[Bug libc/4943] Inconsistent rounding behaviour for sprintf and IEEE doubles
- From: "paul at inet dot co dot za" <sourceware-bugzilla at sourceware dot org>
- To: glibc-bugs at sources dot redhat dot com
- Date: 23 Sep 2007 16:41:25 -0000
- Subject: [Bug libc/4943] Inconsistent rounding behaviour for sprintf and IEEE doubles
- References: <20070820142205.4943.paul@inet.co.za>
- Reply-to: sourceware-bugzilla at sourceware dot org
------- 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.