From: cilibrar@... (Rudi Cilibrasi) Date: 2003-04-19T20:57:06+09:00 Subject: Re: Roundoff problem with Float and Marshal Hi Matz and Nobu again, This is a tricky problem. I have rewritten my patch in a way that I hope is endian-independent, and furthermore allows you to load and save exactly across any IEEE754 system, whether big or little endian floats. Unfortunately, I could find only one bigendian system to test on, and when my friend tried to compile ruby on it, it failed, so I was unable to complete the testing on this new version of the patch. (against the current version in the CVS tree) Maybe there is some kind soul out there that would like to help? I also took Matz suggestion and followed Nobu's example of how to properly put the special two bytes at the end of the string after the nul for backwards compatibility. All I can say with confidence is that this patch also passes all my tests on the x86 linux machines at my disposal. Cheers, Rudi P.S. I am embarrassed to use some extra globals to make this patch work. --- marshal.c 2003-04-19 12:48:14.000000000 +0200 +++ marshal.c 2003-04-19 12:42:48.000000000 +0200 @@ -182,4 +182,72 @@ w_long(x, arg) } +#define FT_OTHER 0 +#define FT_IEEE754 1 +#define FT_UNDEFINED 5 + +static int floatingType = FT_UNDEFINED; +static int floatingStart, floatingDir; /* forgive me */ + +static void checkFloatingType() +{ + if (floatingType == FT_UNDEFINED) { + volatile double t = 1.234; + volatile unsigned char *b = (unsigned char *) &t; + if (b[0] == 0x58 && b[1] == 0x39 && b[2] == 0xb4 && b[3] == 0xc8 && + b[4] == 0x76 && b[5] == 0xbe && b[6] == 0xf3 && b[7] == 0x3f) { + floatingType = FT_IEEE754; + floatingStart = 0; + floatingDir = 1; + } + if (b[7] == 0x58 && b[6] == 0x39 && b[5] == 0xb4 && b[4] == 0xc8 && + b[3] == 0x76 && b[2] == 0xbe && b[1] == 0xf3 && b[0] == 0x3f) { + floatingType = FT_IEEE754; + floatingStart = 7; + floatingDir = -1; + } + if (floatingType == FT_UNDEFINED) + floatingType = FT_OTHER; + } + return; +} + +#define GETBYTE(d, x) ((unsigned char *) &d)[floatingStart + floatingDir * x] + +static double fixMantissaBits(double d, unsigned char *buf) +{ + int encodedType = buf[1] >> 6; + checkFloatingType(); + if (floatingType != encodedType) + return d; + switch(floatingType) { + case FT_IEEE754: + GETBYTE(d,0) = buf[0]; + GETBYTE(d,1) &= 0xc0; + GETBYTE(d,1) |= buf[1] & ~0xc0; + break; + default: + break; + } + return d; +} + +static double saveMantissaBits(double d, unsigned char *buf) +{ + unsigned char result; + checkFloatingType(); + switch (floatingType) { + case FT_IEEE754: + buf[0] = GETBYTE(d, 0); + buf[1] = GETBYTE(d, 1); + break; + default: + buf[0] = 1; + buf[1] = 1; + break; + } + buf[1] = (buf[1] & ~0xc0) | (floatingType << 6); +} + + static void w_float(d, arg) @@ -202,7 +270,8 @@ w_float(d, arg) else { /* xxx: should not use system's sprintf(3) */ - sprintf(buf, "%.16g", d); + sprintf(buf, "%.17g", d); + saveMantissaBits(d, buf+strlen(buf)+1); } - w_bytes(buf, strlen(buf), arg); + w_bytes(buf, strlen(buf)+3, arg); } @@ -931,4 +1000,5 @@ r_object0(arg, proc) else { d = strtod(RSTRING(str)->ptr, 0); + d = fixMantissaBits(d, RSTRING(str)->ptr + strlen(RSTRING(str)->ptr) + 1); } v = rb_float_new(d);