summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornaruse <naruse@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-04-01 04:32:57 +0000
committernaruse <naruse@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-04-01 04:32:57 +0000
commit12b2e16e21bbd7f0b6fd12c7e21cba4d42e284ad (patch)
tree20346cc6c455bba7684139a3c2af88806a9ba7b4
parent4d399f12d4354c17bbb1ea2b12de4c11c6ac03ff (diff)
* 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
-rw-r--r--ChangeLog13
-rw-r--r--LEGAL26
-rw-r--r--missing/vsnprintf.c41
-rw-r--r--sprintf.c16
-rw-r--r--test/ruby/test_sprintf.rb13
-rw-r--r--util.c143
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 <naruse@ruby-lang.org>
+
+ * 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 <naruse@ruby-lang.org>
* 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 <das@FreeBSD.ORG>
+ 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 <das@FreeBSD.ORG>
+ * 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
{