Hi,

At Fri, 18 Apr 2003 18:33:37 +0900,
Yukihiro Matsumoto wrote:
> |I have implemented the idea I gave earlier.  I had to use 2 bytes to
> |pass all my test cases.  There is a 2-bit space for other floating formats
> |also.  I hope this technique preserves portability as well as before,
> |yet also gives numerical stability which is essential for some types of
> |scientific computing, e.g. distributed and long-term simulations.
> |What do you think of this solution?
> 
> Interesting.  Is this endian safe, i.e. does this work properly on
> both little endian and big endian machine?

No, unfortunately, and assuming binary format is less portable.

> If it is endian safe, then we can put mantissa bits at the tail of
> float representation, e.g. "2.3847655 \001\001", to make it work well
> with older Marshal.

Implemented with "\0" separator.


Index: marshal.c =================================================================== RCS file: //sharui/cvs/ruby/src/ruby/marshal.c,v retrieving revision 1.84 diff -u -2 -p -r1.84 marshal.c --- marshal.c 9 Apr 2003 05:08:25 -0000 1.84 +++ marshal.c 18 Apr 2003 11:22:01 -0000 @@ -17,4 +17,7 @@ #include <math.h> +#ifdef HAVE_FLOAT_H +#include <float.h> +#endif #define BITSPERSHORT (2*CHAR_BIT) @@ -182,4 +185,49 @@ w_long(x, arg) } +#ifdef DBL_MANT_DIG +static double +load_least_mantissa(d, buf) + double d; + char *buf; +{ + int e, m, s = 0; + + if (d < 0) { + d = -d; + s = 1; + } + modf(ldexp(frexp(d, &e), DBL_MANT_DIG - 16), &d); + m = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff); + d = ldexp(frexp(d + ldexp((double)m, -16), &m), e); + if (s) { + d = -d; + } + return d; +} + +static int +save_least_mantissa(d, buf) + double d; + char *buf; +{ + int e, m; + + m = (int)(modf(ldexp(frexp(fabs(d), &e), DBL_MANT_DIG - 16), &d) * 0x10000); + *buf++ = 0; + *buf++ = m >> 8; + *buf++ = m; + return 3; +} +#else +#define load_least_mantissa(d, buf) (d) +#define save_least_mantissa(d, buf) 0 +#endif + +#ifdef DBL_DIG +#define FLOAT_DIG (DBL_DIG+1) +#else +#define FLOAT_DIG 17 +#endif + static void w_float(d, arg) @@ -188,21 +236,27 @@ w_float(d, arg) { char buf[100]; + int len; if (isinf(d)) { if (d < 0) strcpy(buf, "-inf"); else strcpy(buf, "inf"); + len = strlen(buf); } else if (isnan(d)) { strcpy(buf, "nan"); + len = strlen(buf); } else if (d == 0.0) { if (1.0/d < 0) strcpy(buf, "-0"); else strcpy(buf, "0"); + len = strlen(buf); } else { /* xxx: should not use system's sprintf(3) */ - sprintf(buf, "%.16g", d); + sprintf(buf, "%.*g", FLOAT_DIG, d); + len = strlen(buf); + len += save_least_mantissa(d, buf + len); } - w_bytes(buf, strlen(buf), arg); + w_bytes(buf, len, arg); } @@ -930,5 +984,9 @@ r_object0(arg, proc) } else { - d = strtod(RSTRING(str)->ptr, 0); + char *e; + d = strtod(RSTRING(str)->ptr, &e); + if (!*e++ && e - RSTRING(str)->ptr < RSTRING(str)->len) { + d = load_least_mantissa(d, e); + } } v = rb_float_new(d);
-- Nobu Nakada