diff options
Diffstat (limited to 'gnu/usr.bin/perl/sv.c')
-rw-r--r-- | gnu/usr.bin/perl/sv.c | 223 |
1 files changed, 161 insertions, 62 deletions
diff --git a/gnu/usr.bin/perl/sv.c b/gnu/usr.bin/perl/sv.c index decc47c3dd4..90b2ced3270 100644 --- a/gnu/usr.bin/perl/sv.c +++ b/gnu/usr.bin/perl/sv.c @@ -9706,6 +9706,8 @@ Perl_sv_resetpvn(pTHX_ const char *s, STRLEN len, HV * const stash) if (!todo[(U8)*HeKEY(entry)]) continue; gv = MUTABLE_GV(HeVAL(entry)); + if (!isGV(gv)) + continue; sv = GvSV(gv); if (sv && !SvREADONLY(sv)) { SV_CHECK_THINKFIRST_COW_DROP(sv); @@ -10983,8 +10985,9 @@ Perl_sv_vcatpvfn(pTHX_ SV *const sv, const char *const pat, const STRLEN patlen, * the hexadecimal values (for %a/%A). The nv is the NV where the value * are being extracted from (either directly from the long double in-memory * presentation, or from the uquad computed via frexp+ldexp). frexp also - * is used to update the exponent. vhex is the pointer to the beginning - * of the output buffer (of VHEX_SIZE). + * is used to update the exponent. The subnormal is set to true + * for IEEE 754 subnormals/denormals (including the x86 80-bit format). + * The vhex is the pointer to the beginning of the output buffer of VHEX_SIZE. * * The tricky part is that S_hextract() needs to be called twice: * the first time with vend as NULL, and the second time with vend as @@ -10994,14 +10997,15 @@ Perl_sv_vcatpvfn(pTHX_ SV *const sv, const char *const pat, const STRLEN patlen, * (the extraction of the hexadecimal values) takes place. * Sanity failures cause fatal failures during both rounds. */ STATIC U8* -S_hextract(pTHX_ const NV nv, int* exponent, U8* vhex, U8* vend) +S_hextract(pTHX_ const NV nv, int* exponent, bool *subnormal, + U8* vhex, U8* vend) { U8* v = vhex; int ix; int ixmin = 0, ixmax = 0; - /* XXX Inf/NaN/denormal handling in the HEXTRACT_IMPLICIT_BIT, - * and elsewhere. */ + /* XXX Inf/NaN are not handled here, since it is + * assumed they are to be output as "Inf" and "NaN". */ /* These macros are just to reduce typos, they have multiple * repetitions below, but usually only one (or sometimes two) @@ -11034,13 +11038,20 @@ S_hextract(pTHX_ const NV nv, int* exponent, U8* vhex, U8* vend) for (ix = a; ix >= b; ix--) { HEXTRACT_BYTE(ix); } #define HEXTRACT_BYTES_BE(a, b) \ for (ix = a; ix <= b; ix++) { HEXTRACT_BYTE(ix); } +#define HEXTRACT_GET_SUBNORMAL(nv) *subnormal = Perl_fp_class_denorm(nv) #define HEXTRACT_IMPLICIT_BIT(nv) \ STMT_START { \ - if (vend) *v++ = ((nv) == 0.0) ? 0 : 1; else v++; \ + if (!*subnormal) { \ + if (vend) *v++ = ((nv) == 0.0) ? 0 : 1; else v++; \ + } \ } STMT_END -/* Most formats do. Those which don't should undef this. */ +/* Most formats do. Those which don't should undef this. + * + * But also note that IEEE 754 subnormals do not have it, or, + * expressed alternatively, their implicit bit is zero. */ #define HEXTRACT_HAS_IMPLICIT_BIT + /* Many formats do. Those which don't should undef this. */ #define HEXTRACT_HAS_TOP_NYBBLE @@ -11054,6 +11065,7 @@ S_hextract(pTHX_ const NV nv, int* exponent, U8* vhex, U8* vend) const U8* vmaxend = vhex + HEXTRACTSIZE; PERL_UNUSED_VAR(ix); /* might happen */ (void)Perl_frexp(PERL_ABS(nv), exponent); + *subnormal = FALSE; if (vend && (vend <= vhex || vend > vmaxend)) { /* diag_listed_as: Hexadecimal float: internal error (%s) */ Perl_croak(aTHX_ "Hexadecimal float: internal error (entry)"); @@ -11063,10 +11075,11 @@ S_hextract(pTHX_ const NV nv, int* exponent, U8* vhex, U8* vend) #if defined(USE_LONG_DOUBLE) && (NVSIZE > DOUBLESIZE) # if LONG_DOUBLEKIND == LONG_DOUBLE_IS_IEEE_754_128_BIT_LITTLE_ENDIAN /* Used in e.g. VMS and HP-UX IA-64, e.g. -0.1L: - * 9a 99 99 99 99 99 99 99 99 99 99 99 99 99 fb 3f */ + * 9a 99 99 99 99 99 99 99 99 99 99 99 99 99 fb bf */ /* The bytes 13..0 are the mantissa/fraction, * the 15,14 are the sign+exponent. */ const U8* nvp = (const U8*)(&nv); + HEXTRACT_GET_SUBNORMAL(nv); HEXTRACT_IMPLICIT_BIT(nv); # undef HEXTRACT_HAS_TOP_NYBBLE HEXTRACT_BYTES_LE(13, 0); @@ -11076,18 +11089,21 @@ S_hextract(pTHX_ const NV nv, int* exponent, U8* vhex, U8* vend) /* The bytes 2..15 are the mantissa/fraction, * the 0,1 are the sign+exponent. */ const U8* nvp = (const U8*)(&nv); + HEXTRACT_GET_SUBNORMAL(nv); HEXTRACT_IMPLICIT_BIT(nv); # undef HEXTRACT_HAS_TOP_NYBBLE HEXTRACT_BYTES_BE(2, 15); # elif LONG_DOUBLEKIND == LONG_DOUBLE_IS_X86_80_BIT_LITTLE_ENDIAN /* x86 80-bit "extended precision", 64 bits of mantissa / fraction / - * significand, 15 bits of exponent, 1 bit of sign. NVSIZE can - * be either 12 (ILP32, Solaris x86) or 16 (LP64, Linux and OS X), - * meaning that 2 or 6 bytes are empty padding. */ - /* The bytes 7..0 are the mantissa/fraction */ + * significand, 15 bits of exponent, 1 bit of sign. No implicit bit. + * NVSIZE can be either 12 (ILP32, Solaris x86) or 16 (LP64, Linux + * and OS X), meaning that 2 or 6 bytes are empty padding. */ + /* The bytes 0..1 are the sign+exponent, + * the bytes 2..9 are the mantissa/fraction. */ const U8* nvp = (const U8*)(&nv); # undef HEXTRACT_HAS_IMPLICIT_BIT # undef HEXTRACT_HAS_TOP_NYBBLE + HEXTRACT_GET_SUBNORMAL(nv); HEXTRACT_BYTES_LE(7, 0); # elif LONG_DOUBLEKIND == LONG_DOUBLE_IS_X86_80_BIT_BIG_ENDIAN /* Does this format ever happen? (Wikipedia says the Motorola @@ -11097,6 +11113,7 @@ S_hextract(pTHX_ const NV nv, int* exponent, U8* vhex, U8* vend) const U8* nvp = (const U8*)(&nv); # undef HEXTRACT_HAS_IMPLICIT_BIT # undef HEXTRACT_HAS_TOP_NYBBLE + HEXTRACT_GET_SUBNORMAL(nv); HEXTRACT_BYTES_BE(0, 7); # else # define HEXTRACT_FALLBACK @@ -11132,18 +11149,21 @@ S_hextract(pTHX_ const NV nv, int* exponent, U8* vhex, U8* vend) # ifdef HEXTRACT_LITTLE_ENDIAN /* 0 1 2 3 4 5 6 7 (MSB = 7, LSB = 0, 6+7 = exponent+sign) */ const U8* nvp = (const U8*)(&nv); + HEXTRACT_GET_SUBNORMAL(nv); HEXTRACT_IMPLICIT_BIT(nv); HEXTRACT_TOP_NYBBLE(6); HEXTRACT_BYTES_LE(5, 0); # elif defined(HEXTRACT_BIG_ENDIAN) /* 7 6 5 4 3 2 1 0 (MSB = 7, LSB = 0, 6+7 = exponent+sign) */ const U8* nvp = (const U8*)(&nv); + HEXTRACT_GET_SUBNORMAL(nv); HEXTRACT_IMPLICIT_BIT(nv); HEXTRACT_TOP_NYBBLE(1); HEXTRACT_BYTES_BE(2, 7); # elif DOUBLEKIND == DOUBLE_IS_IEEE_754_64_BIT_MIXED_ENDIAN_LE_BE /* 4 5 6 7 0 1 2 3 (MSB = 7, LSB = 0, 6:7 = nybble:exponent:sign) */ const U8* nvp = (const U8*)(&nv); + HEXTRACT_GET_SUBNORMAL(nv); HEXTRACT_IMPLICIT_BIT(nv); HEXTRACT_TOP_NYBBLE(2); /* 6 */ HEXTRACT_BYTE(1); /* 5 */ @@ -11155,6 +11175,7 @@ S_hextract(pTHX_ const NV nv, int* exponent, U8* vhex, U8* vend) # elif DOUBLEKIND == DOUBLE_IS_IEEE_754_64_BIT_MIXED_ENDIAN_BE_LE /* 3 2 1 0 7 6 5 4 (MSB = 7, LSB = 0, 7:6 = sign:exponent:nybble) */ const U8* nvp = (const U8*)(&nv); + HEXTRACT_GET_SUBNORMAL(nv); HEXTRACT_IMPLICIT_BIT(nv); HEXTRACT_TOP_NYBBLE(5); /* 6 */ HEXTRACT_BYTE(6); /* 5 */ @@ -11171,6 +11192,7 @@ S_hextract(pTHX_ const NV nv, int* exponent, U8* vhex, U8* vend) # endif #endif /* #if defined(USE_LONG_DOUBLE) && (NVSIZE > DOUBLESIZE) #else */ # ifdef HEXTRACT_FALLBACK + HEXTRACT_GET_SUBNORMAL(nv); # undef HEXTRACT_HAS_TOP_NYBBLE /* Meaningless, but consistent. */ /* The fallback is used for the double-double format, and * for unknown long double formats, and for unknown double @@ -12296,7 +12318,13 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p need = BIT_DIGITS(i); } /* if i < 0, the number of digits is hard to predict. */ } - need += has_precis ? precis : 6; /* known default */ + + { + STRLEN pr = has_precis ? precis : 6; /* known default */ + if (need >= ((STRLEN)~0) - pr) + croak_memory_wrap(); + need += pr; + } if (need < width) need = width; @@ -12367,10 +12395,12 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p #endif /* HAS_LDBL_SPRINTF_BUG */ - need += 20; /* fudge factor */ + if (need >= ((STRLEN)~0) - 40) + croak_memory_wrap(); + need += 40; /* fudge factor */ if (PL_efloatsize < need) { Safefree(PL_efloatbuf); - PL_efloatsize = need + 20; /* more fudge */ + PL_efloatsize = need; Newx(PL_efloatbuf, PL_efloatsize, char); PL_efloatbuf[0] = '\0'; } @@ -12402,6 +12432,7 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p U8* vend; /* pointer to one beyond last digit of vhex */ U8* vfnz = NULL; /* first non-zero */ U8* vlnz = NULL; /* last non-zero */ + U8* v0 = NULL; /* first output */ const bool lower = (c == 'a'); /* At output the values of vhex (up to vend) will * be mapped through the xdig to get the actual @@ -12410,33 +12441,47 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p int zerotail = 0; /* how many extra zeros to append */ int exponent = 0; /* exponent of the floating point input */ bool hexradix = FALSE; /* should we output the radix */ + bool subnormal = FALSE; /* IEEE 754 subnormal/denormal */ + bool negative = FALSE; - /* XXX: denormals, NaN, Inf. + /* XXX: NaN, Inf -- though they are printed as "NaN" and "Inf". * * For example with denormals, (assuming the vanilla * 64-bit double): the exponent is zero. 1xp-1074 is * the smallest denormal and the smallest double, it - * should be output as 0x0.0000000000001p-1022 to + * could be output also as 0x0.0000000000001p-1022 to * match its internal structure. */ - vend = S_hextract(aTHX_ nv, &exponent, vhex, NULL); - S_hextract(aTHX_ nv, &exponent, vhex, vend); + vend = S_hextract(aTHX_ nv, &exponent, &subnormal, vhex, NULL); + S_hextract(aTHX_ nv, &exponent, &subnormal, vhex, vend); #if NVSIZE > DOUBLESIZE # ifdef HEXTRACT_HAS_IMPLICIT_BIT /* In this case there is an implicit bit, - * and therefore the exponent is shifted shift by one. */ + * and therefore the exponent is shifted by one. */ exponent--; # else - /* In this case there is no implicit bit, - * and the exponent is shifted by the first xdigit. */ - exponent -= 4; +# ifdef NV_X86_80_BIT + if (subnormal) { + /* The subnormals of the x86-80 have a base exponent of -16382, + * (while the physical exponent bits are zero) but the frexp() + * returned the scientific-style floating exponent. We want + * to map the last one as: + * -16831..-16384 -> -16382 (the last normal is 0x1p-16382) + * -16835..-16388 -> -16384 + * since we want to keep the first hexdigit + * as one of the [8421]. */ + exponent = -4 * ( (exponent + 1) / -4) - 2; + } else { + exponent -= 4; + } +# endif + /* TBD: other non-implicit-bit platforms than the x86-80. */ # endif #endif - if (fv < 0 - || Perl_signbit(nv) - ) + negative = fv < 0 || Perl_signbit(nv); + if (negative) *p++ = '-'; else if (plus) *p++ = plus; @@ -12471,50 +12516,98 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p exponent--; #endif - if (precis > 0) { - if ((SSize_t)(precis + 1) < vend - vhex) { - bool round; - - v = vhex + precis + 1; - /* Round away from zero: if the tail - * beyond the precis xdigits is equal to - * or greater than 0x8000... */ - round = *v > 0x8; - if (!round && *v == 0x8) { - for (v++; v < vend; v++) { - if (*v) { - round = TRUE; - break; - } + if (subnormal) { +#ifndef NV_X86_80_BIT + if (vfnz[0] > 1) { + /* IEEE 754 subnormals (but not the x86 80-bit): + * we want "normalize" the subnormal, + * so we need to right shift the hex nybbles + * so that the output of the subnormal starts + * from the first true bit. (Another, equally + * valid, policy would be to dump the subnormal + * nybbles as-is, to display the "physical" layout.) */ + int i, n; + U8 *vshr; + /* Find the ceil(log2(v[0])) of + * the top non-zero nybble. */ + for (i = vfnz[0], n = 0; i > 1; i >>= 1, n++) { } + assert(n < 4); + vlnz[1] = 0; + for (vshr = vlnz; vshr >= vfnz; vshr--) { + vshr[1] |= (vshr[0] & (0xF >> (4 - n))) << (4 - n); + vshr[0] >>= n; + } + if (vlnz[1]) { + vlnz++; + } + } +#endif + v0 = vfnz; + } else { + v0 = vhex; + } + + if (has_precis) { + U8* ve = (subnormal ? vlnz + 1 : vend); + SSize_t vn = ve - (subnormal ? vfnz : vhex); + if ((SSize_t)(precis + 1) < vn) { + bool overflow = FALSE; + if (v0[precis + 1] < 0x8) { + /* Round down, nothing to do. */ + } else if (v0[precis + 1] > 0x8) { + /* Round up. */ + v0[precis]++; + overflow = v0[precis] > 0xF; + v0[precis] &= 0xF; + } else { /* v0[precis] == 0x8 */ + /* Half-point: round towards the one + * with the even least-significant digit: + * 08 -> 0 88 -> 8 + * 18 -> 2 98 -> a + * 28 -> 2 a8 -> a + * 38 -> 4 b8 -> c + * 48 -> 4 c8 -> c + * 58 -> 6 d8 -> e + * 68 -> 6 e8 -> e + * 78 -> 8 f8 -> 10 */ + if ((v0[precis] & 0x1)) { + v0[precis]++; } + overflow = v0[precis] > 0xF; + v0[precis] &= 0xF; } - if (round) { - for (v = vhex + precis; v >= vhex; v--) { - if (*v < 0xF) { - (*v)++; + + if (overflow) { + for (v = v0 + precis - 1; v >= v0; v--) { + (*v)++; + overflow = *v > 0xF; + (*v) &= 0xF; + if (!overflow) { break; } - *v = 0; - if (v == vhex) { - /* If the carry goes all the way to - * the front, we need to output - * a single '1'. This goes against - * the "xdigit and then radix" - * but since this is "cannot happen" - * category, that is probably good. */ - *p++ = xdig[1]; - } + } + if (v == v0 - 1 && overflow) { + /* If the overflow goes all the + * way to the front, we need to + * insert 0x1 in front, and adjust + * the exponent. */ + Move(v0, v0 + 1, vn, char); + *v0 = 0x1; + exponent += 4; } } + /* The new effective "last non zero". */ - vlnz = vhex + precis; + vlnz = v0 + precis; } else { - zerotail = precis - (vlnz - vhex); + zerotail = + subnormal ? precis - vn + 1 : + precis - (vlnz - vhex); } } - v = vhex; + v = v0; *p++ = xdig[*v++]; /* If there are non-zero xdigits, the radix @@ -12574,12 +12667,18 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p memset(PL_efloatbuf + elen, ' ', width - elen); } else if (fill == '0') { - /* Insert the zeros between the "0x" and - * the digits, otherwise we end up with - * "0000xHHH..." */ + /* Insert the zeros after the "0x" and the + * the potential sign, but before the digits, + * otherwise we end up with "0000xH.HHH...", + * when we want "0x000H.HHH..." */ STRLEN nzero = width - elen; char* zerox = PL_efloatbuf + 2; - Move(zerox, zerox + nzero, elen - 2, char); + STRLEN nmove = elen - 2; + if (negative || plus) { + zerox++; + nmove--; + } + Move(zerox, zerox + nzero, nmove, char); memset(zerox, fill, nzero); } else { |