summaryrefslogtreecommitdiff
path: root/numeric.c
diff options
context:
space:
mode:
authorEd Allen Smith <easmith@beatrice.rutgers.edu>2010-10-03 15:51:34 -0700
committerFather Chrysostomos <sprout@cpan.org>2010-10-03 15:51:59 -0700
commitffa277e59cf0a21b8d133be39d7e10fad0154ae8 (patch)
treec5125d1d73a3ba4a6c77b95576143ba263c5008f /numeric.c
parent6d24fbd10b671f02fd7526bee66bd955389d9ee4 (diff)
downloadperl-ffa277e59cf0a21b8d133be39d7e10fad0154ae8.tar.gz
[perl #32380] numeric.c assumes that NV_DIG+2 will be enough digits
for all precision possible in NV numeric.c, in the Perl_my_atof2 function, makes the following assumption: /* There is no point in processing more significant digits * than the NV can hold. Note that NV_DIG is a lower-bound value, * while we need an upper-bound value. We add 2 to account for this; * since it will have been conservative on both the first and last digit. * For example a 32-bit mantissa with an exponent of 4 would have * exact values in the set * 4 * 8 * .. * 17179869172 * 17179869176 * 17179869180 * * where for the purposes of calculating NV_DIG we would have to discount * both the first and last digit, since neither can hold all values from * 0..9; but for calculating the value we must examine those two digits. */ #define MAX_SIG_DIGITS (NV_DIG+2) Digits beyond MAX_SIG_DIGITS are ignored. In some systems and/or modes (e.g., with/without using long doubles), this is not the case. One example is IRIX when using long doubles, which are not fully IEEE compliant; with it, while NV_DIG (the _minimum_ number of digits usable) is 31 for long doubles used as NVs, long doubles can have up to 34 digits of accuracy. (As well as IRIX with long doubles, other machines using a mode in which NV is not IEEE compliant (e.g., as found by the following from numeric.c: #ifdef ((defined(VMS) && !defined(__IEEE_FP)) || defined(_UNICOS)) (although UNICOS does not by default use Perl's atof in any event) or as noted in the hints files for DEC OSF with the old MIPS CC) may benefit from a change to this.) I will attach a test program, example set of problematic outputs, and experimental patch so others can explore this on their systems. (With the patch and a -Accflags='-DMAX_SIG_DIG_PLUS=3' (or -Accflags='-DMAX_SIG_DIG_PLUS=4'), the test program was successful. Different values of MAX_SIG_DIG_PLUS may need to be experimented with, especially with different other Configure/compiler flags (long doubles yes/no, optimization affecting floating point, etcetera); 3 was the maximum that did any good on IRIX with long doubles, but others may differ.) I have done some local testing (as in the normal testsuite) of the patch and different -DMAX_SIG_DIG_PLUS values, and will be doing more; it is possible that it would be best to build in the test program into, say, numconvert.t. Patching hints/irix_6.sh to use -DMAX_SIG_DIG_PLUS=3 if long doubles are in use, or a define of MAX_SIG_DIG_PLUS to 3 if defined(IRIX) and long doubles are in use, is also advisable; I have not included either in my patch because I was unsure which was recommended practice.
Diffstat (limited to 'numeric.c')
-rw-r--r--numeric.c9
1 files changed, 8 insertions, 1 deletions
diff --git a/numeric.c b/numeric.c
index c9423ce3f3..505b1ce2ca 100644
--- a/numeric.c
+++ b/numeric.c
@@ -887,7 +887,14 @@ Perl_my_atof2(pTHX_ const char* orig, NV* value)
* both the first and last digit, since neither can hold all values from
* 0..9; but for calculating the value we must examine those two digits.
*/
-#define MAX_SIG_DIGITS (NV_DIG+2)
+#ifdef MAX_SIG_DIG_PLUS
+ /* It is not necessarily the case that adding 2 to NV_DIG gets all the
+ possible digits in a NV, especially if NVs are not IEEE compliant
+ (e.g., long doubles on IRIX) - Allen <allens@cpan.org> */
+# define MAX_SIG_DIGITS (NV_DIG+MAX_SIG_DIG_PLUS)
+#else
+# define MAX_SIG_DIGITS (NV_DIG+2)
+#endif
/* the max number we can accumulate in a UV, and still safely do 10*N+9 */
#define MAX_ACCUMULATE ( (UV) ((UV_MAX - 9)/10))