From 12b2e16e21bbd7f0b6fd12c7e21cba4d42e284ad Mon Sep 17 00:00:00 2001 From: naruse Date: Thu, 1 Apr 2010 04:32:57 +0000 Subject: * sprintf.c (rb_str_format): support %a format. [ruby-dev:40650] * missing/vsnprintf.c (BSD_vfprintf): ditto. * missing/vsnprintf.c (cvt): ditto. * util.c (BSD__hdtoa): added. This is 2-clause BSDL licensed by David Schultz and from FreeBSD. * LEGAL: add about hdtoa() in util.c. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27141 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 13 +++++ LEGAL | 26 +++++++++ missing/vsnprintf.c | 41 +++++++++++-- sprintf.c | 16 ++++-- test/ruby/test_sprintf.rb | 13 +++++ util.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 242 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 51d375a4d7..21d237beb2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +Thu Apr 1 13:30:12 2010 NARUSE, Yui + + * sprintf.c (rb_str_format): support %a format. [ruby-dev:40650] + + * missing/vsnprintf.c (BSD_vfprintf): ditto. + + * missing/vsnprintf.c (cvt): ditto. + + * util.c (BSD__hdtoa): added. This is 2-clause BSDL licensed + by David Schultz and from FreeBSD. + + * LEGAL: add about hdtoa() in util.c. + Thu Apr 1 13:24:12 2010 NARUSE, Yui * object.c (rb_cstr_to_dbl): return 0.0 if hexadecimal and diff --git a/LEGAL b/LEGAL index 991bb4dea9..c28a0b5d74 100644 --- a/LEGAL +++ b/LEGAL @@ -123,6 +123,32 @@ win32/win32.[ch]: You may distribute under the terms of either the GNU General Public License or the Artistic License, as specified in the perl README file. +util.c (partly): + + Copyright (c) 2004-2008 David Schultz + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + random.c This file is under the new-style BSD license. diff --git a/missing/vsnprintf.c b/missing/vsnprintf.c index 4e19651679..acb09c9454 100644 --- a/missing/vsnprintf.c +++ b/missing/vsnprintf.c @@ -559,7 +559,7 @@ BSD_vfprintf(FILE *fp, const char *fmt0, va_list ap) struct __suio uio; /* output information: summary */ struct __siov iov[NIOV];/* ... and individual io vectors */ char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ - char ox[2]; /* space for 0x hex-prefix */ + char ox[4]; /* space for 0x hex-prefix, hexadecimal's 1. */ char *const ebuf = buf + sizeof(buf); #if SIZEOF_LONG > SIZEOF_INT long ln; @@ -784,6 +784,11 @@ reswitch: switch (ch) { base = 10; goto number; #ifdef FLOATING_POINT + case 'a': + case 'A': + if (prec >= 0) + prec++; + goto fp_begin; case 'e': /* anomalous precision */ case 'E': if (prec != 0) @@ -822,7 +827,12 @@ fp_begin: _double = va_arg(ap, double); else ch = 'g'; } - if (ch <= 'e') { /* 'e' or 'E' fmt */ + if (ch == 'a' || ch == 'A') { + --expt; + expsize = exponent(expstr, expt, ch + 'p' - 'a'); + size = expsize + ndig; + } + else if (ch <= 'e') { /* 'e' or 'E' fmt */ --expt; expsize = exponent(expstr, expt, ch); size = expsize + ndig; @@ -1048,7 +1058,20 @@ long_len: if ((flags & FPT) == 0) { PRINT(cp, fieldsz); } else { /* glue together f_p fragments */ - if (ch >= 'f') { /* 'f' or 'g' */ + if (ch == 'a' || ch == 'A') { + ox[0] = '0'; + ox[1] = ch + ('x' - 'a'); + PRINT(ox, 2); + if (ndig > 1 || flags & ALT) { + ox[2] = *cp++; + ox[3] = '.'; + PRINT(ox+2, 2); + PRINT(cp, ndig-1); + } else /* XpYYY */ + PRINT(cp, 1); + PRINT(expstr, expsize); + } + else if (ch >= 'f') { /* 'f' or 'g' */ if (_double == 0) { /* kludge for __dtoa irregularity */ if (ndig <= 1 && @@ -1112,6 +1135,7 @@ error: #ifdef FLOATING_POINT extern char *BSD__dtoa __P((double, int, int, int *, int *, char **)); +extern char *BSD__hdtoa(double, const char *, int, int *, int *, char **); static char * cvt(value, ndigits, flags, sign, decpt, ch, length, buf) @@ -1135,7 +1159,14 @@ cvt(value, ndigits, flags, sign, decpt, ch, length, buf) } else { *sign = '\000'; } - digits = BSD__dtoa(value, mode, ndigits, decpt, &dsgn, &rve); + if (ch == 'a' || ch =='A') { + digits = BSD__hdtoa(value, + ch == 'a' ? "0123456789abcdef" : "0123456789ABCDEF", + ndigits, decpt, &dsgn, &rve); + } + else { + digits = BSD__dtoa(value, mode, ndigits, decpt, &dsgn, &rve); + } memcpy(buf, digits, rve - digits); xfree(digits); rve = buf + (rve - digits); @@ -1181,7 +1212,7 @@ exponent(p0, exp, fmtch) for (; t < expbuf + MAXEXP; *p++ = *t++); } else { - *p++ = '0'; + if (fmtch & 15) *p++ = '0'; /* other than p or P */ *p++ = to_char(exp); } return (int)(p - p0); diff --git a/sprintf.c b/sprintf.c index bec0569b5c..e26b833598 100644 --- a/sprintf.c +++ b/sprintf.c @@ -227,6 +227,10 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv) * | equal to the precision, or in dd.dddd form otherwise. * | The precision specifies the number of significant digits. * G | Equivalent to `g', but use an uppercase `E' in exponent form. + * a | Convert floating point argument as [-]0xh.hhhhp[+-]dd, + * | which is consisted from optional sign, "0x", fraction part + * | as hexadecimal, "p", and exponential part as decimal. + * A | Equivalent to `a', but use uppercase `X' and `P'. * * Field | Other Format * ------+-------------------------------------------------------------- @@ -244,7 +248,7 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv) * Flag | Applies to | Meaning * ---------+---------------+----------------------------------------- * space | bBdiouxX | Leave a space at the start of - * | eEfgG | non-negative numbers. + * | aAeEfgG | non-negative numbers. * | (numeric fmt) | For `o', `x', `X', `b' and `B', use * | | a minus sign with absolute value for * | | negative values. @@ -255,19 +259,19 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv) * | | sprintf string. * ---------+---------------+----------------------------------------- * # | bBoxX | Use an alternative format. - * | eEfgG | For the conversions `o', increase the precision + * | aAeEfgG | For the conversions `o', increase the precision * | | until the first digit will be `0' if * | | it is not formatted as complements. * | | For the conversions `x', `X', `b' and `B' * | | on non-zero, prefix the result with ``0x'', * | | ``0X'', ``0b'' and ``0B'', respectively. - * | | For `e', `E', `f', `g', and 'G', + * | | For `a', `A', `e', `E', `f', `g', and 'G', * | | force a decimal point to be added, * | | even if no digits follow. * | | For `g' and 'G', do not remove trailing zeros. * ---------+---------------+----------------------------------------- * + | bBdiouxX | Add a leading plus sign to non-negative - * | eEfgG | numbers. + * | aAeEfgG | numbers. * | (numeric fmt) | For `o', `x', `X', `b' and `B', use * | | a minus sign with absolute value for * | | negative values. @@ -275,7 +279,7 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv) * - | all | Left-justify the result of this conversion. * ---------+---------------+----------------------------------------- * 0 (zero) | bBdiouxX | Pad with zeros, not spaces. - * | eEfgG | For `o', `x', `X', `b' and `B', radix-1 + * | aAeEfgG | For `o', `x', `X', `b' and `B', radix-1 * | (numeric fmt) | is used for negative numbers formatted as * | | complements. * ---------+---------------+----------------------------------------- @@ -983,6 +987,8 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) case 'G': case 'e': case 'E': + case 'a': + case 'A': { VALUE val = GETARG(); double fval; diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb index 15424d98ac..f8dbbd511e 100644 --- a/test/ruby/test_sprintf.rb +++ b/test/ruby/test_sprintf.rb @@ -191,6 +191,19 @@ class TestSprintf < Test::Unit::TestCase assert_equal(" Inf", sprintf("% 0e", 1.0/0.0), "moved from btest/knownbug") end + def test_float_hex + assert_equal("-0x0p+0", sprintf("%a", -0.0)) + assert_equal("0x0p+0", sprintf("%a", 0.0)) + assert_equal("0x1p-1", sprintf("%a", 0.5)) + assert_equal("0x1p+0", sprintf("%a", 1.0)) + assert_equal("0x1p+1", sprintf("%a", 2.0)) + assert_equal("0x1.193ea7aad030ap+0", sprintf("%a", Math.log(3))) + assert_equal("0X1.193EA7AAD030AP+0", sprintf("%A", Math.log(3))) + assert_equal("0x1p+10", sprintf("%a", 1024)) + assert_equal("0x1.23456p+789", sprintf("%a", 3.704450999893983e+237)) + assert_equal("0x1p-1074", sprintf("%a", 4.9e-324)) + end + BSIZ = 120 def test_skip diff --git a/util.c b/util.c index 11d0272e52..88914be410 100644 --- a/util.c +++ b/util.c @@ -3857,6 +3857,149 @@ ruby_each_words(const char *str, void (*func)(const char*, int, void*), void *ar } } +/*- + * Copyright (c) 2004-2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define DBL_MANH_SIZE 20 +#define DBL_MANL_SIZE 32 +#define INFSTR "Infinity" +#define NANSTR "NaN" +#define DBL_ADJ (DBL_MAX_EXP - 2) +#define SIGFIGS ((DBL_MANT_DIG + 3) / 4 + 1) +#define dexp_get(u) ((int)(word0(u) >> Exp_shift) & ~Exp_msk1) +#define dexp_set(u,v) (word0(u) = (((int)(word0(u)) & ~Exp_mask) | (v << Exp_shift))) +#define dmanh_get(u) ((int)(word0(u) & Frac_mask)) +#define dmanl_get(u) ((int)word1(u)) + + +/* + * This procedure converts a double-precision number in IEEE format + * into a string of hexadecimal digits and an exponent of 2. Its + * behavior is bug-for-bug compatible with dtoa() in mode 2, with the + * following exceptions: + * + * - An ndigits < 0 causes it to use as many digits as necessary to + * represent the number exactly. + * - The additional xdigs argument should point to either the string + * "0123456789ABCDEF" or the string "0123456789abcdef", depending on + * which case is desired. + * - This routine does not repeat dtoa's mistake of setting decpt + * to 9999 in the case of an infinity or NaN. INT_MAX is used + * for this purpose instead. + * + * Note that the C99 standard does not specify what the leading digit + * should be for non-zero numbers. For instance, 0x1.3p3 is the same + * as 0x2.6p2 is the same as 0x4.cp3. This implementation always makes + * the leading digit a 1. This ensures that the exponent printed is the + * actual base-2 exponent, i.e., ilogb(d). + * + * Inputs: d, xdigs, ndigits + * Outputs: decpt, sign, rve + */ +char * +BSD__hdtoa(double d, const char *xdigs, int ndigits, int *decpt, int *sign, + char **rve) +{ + U u; + char *s, *s0; + int bufsize; + uint32_t manh, manl; + + u.d = d; + if (word0(u) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(u) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + + switch (fpclassify(d)) { + case FP_NORMAL: + *decpt = dexp_get(u) - DBL_ADJ; + break; + case FP_ZERO: + *decpt = 1; + return (nrv_alloc("0", rve, 1)); + case FP_SUBNORMAL: + u.d *= 5.363123171977039e+154 /* 0x1p514 */; + *decpt = dexp_get(u) - (514 + DBL_ADJ); + break; + case FP_INFINITE: + *decpt = INT_MAX; + return (nrv_alloc(INFSTR, rve, sizeof(INFSTR) - 1)); + default: /* FP_NAN or unrecognized */ + *decpt = INT_MAX; + return (nrv_alloc(NANSTR, rve, sizeof(NANSTR) - 1)); + } + + /* FP_NORMAL or FP_SUBNORMAL */ + + if (ndigits == 0) /* dtoa() compatibility */ + ndigits = 1; + + /* + * If ndigits < 0, we are expected to auto-size, so we allocate + * enough space for all the digits. + */ + bufsize = (ndigits > 0) ? ndigits : SIGFIGS; + s0 = rv_alloc(bufsize); + + /* Round to the desired number of digits. */ + if (SIGFIGS > ndigits && ndigits > 0) { + float redux = 1.0; + int offset = 4 * ndigits + DBL_MAX_EXP - 4 - DBL_MANT_DIG; + dexp_set(u, offset); + u.d += redux; + u.d -= redux; + *decpt += dexp_get(u) - offset; + } + + manh = dmanh_get(u); + manl = dmanl_get(u); + *s0 = '1'; + for (s = s0 + 1; s < s0 + bufsize; s++) { + *s = xdigs[(manh >> (DBL_MANH_SIZE - 4)) & 0xf]; + manh = (manh << 4) | (manl >> (DBL_MANL_SIZE - 4)); + manl <<= 4; + } + + /* If ndigits < 0, we are expected to auto-size the precision. */ + if (ndigits < 0) { + for (ndigits = SIGFIGS; s0[ndigits - 1] == '0'; ndigits--) + ; + } + + s = s0 + ndigits; + *s = '\0'; + if (rve != NULL) + *rve = s; + return (s0); +} + #ifdef __cplusplus #if 0 { -- cgit v1.2.3