use Config;
-plan(tests => 97);
+plan(tests => 105);
# Test hexfloat literals.
is(0x0.10p0, 0.0625);
is(0x0.100p0, 0.0625);
+is(0x.1p0, 0.0625);
+is(0x1.1p0, 1.0625);
+is(0x1.11p0, 1.06640625);
+is(0x1.111p0, 1.066650390625);
+
# Positive exponents.
is(0x1p2, 4);
is(0x1p+2, 4);
# different from 3e4 cf 30e3 cf 30000. The shifting of the hexdigits
# makes it look stranger, though: 0xap1 == 0x5p2.
+# [perl #127183], try some non-canonical forms.
+SKIP: {
+ skip("nv_preserves_uv_bits is $Config{nv_preserves_uv_bits} not 53", 3)
+ unless ($Config{nv_preserves_uv_bits} == 53);
+ is(0x0.b17217f7d1cf78p0, 0x1.62e42fefa39efp-1);
+ is(0x0.58b90bfbe8e7bcp1, 0x1.62e42fefa39efp-1);
+ is(0x0.2c5c85fdf473dep2, 0x1.62e42fefa39efp-1);
+}
+
# Needs to use within() instead of is() because of long doubles.
within(0x1.99999999999ap-4, 0.1, 1e-9);
within(0x3.333333333333p-5, 0.1, 1e-9);
eval '@a = 0x3..5';
is("@a", "3 4 5");
+ undef $a;
eval '$a = eval "0x.3"';
is($a, '03');
+ undef $a;
eval '$a = eval "0xc.3"';
is($a, '123');
+
+ undef $a;
+ eval '$a = eval "0x.p3"';
+ is($a, undef);
}
# Test warnings.
print "# no hexfloat tests\n";
}
-plan tests => 1408 + ($Q ? 0 : 12) + @hexfloat + 9;
+plan tests => 1408 + ($Q ? 0 : 12) + @hexfloat + 12;
use strict;
use Config;
is(sprintf("%+a", -0.0), "-0x0p+0", "negative zero");
is(sprintf("%.13a", -0.0), "-0x0.0000000000000p+0", "negative zero");
}
+
+SKIP: {
+ # [perl #127183] Non-canonical hexadecimal floats are parsed prematurely
+
+ skip("nv_preserves_uv_bits is $Config{nv_preserves_uv_bits}, not 53", 3)
+ unless $Config{nv_preserves_uv_bits} == 53;
+
+ {
+ # The 0x0.b17217f7d1cf78p0 is the original LHS value
+ # from [perl #127183], its bits are 0x162e42fefa39ef << 3,
+ # resulting in a non-canonical form of hexfp, where the most
+ # significant bit is zero, instead of one.
+ is(sprintf("%a", 0x0.b17217f7d1cf78p0 - 0x1.62e42fefa39efp-1),
+ "0x0p+0",
+ "non-canonical form [perl #127183]");
+ }
+
+ {
+ no warnings 'overflow'; # Not the point here.
+
+ # The 0x058b90bfbe8e7bc is 0x162e42fefa39ef << 2,
+ # the 0x02c5c85fdf473de is 0x162e42fefa39ef << 1,
+ # see above.
+ is(sprintf("%a", 0x0.58b90bfbe8e7bcp1 - 0x1.62e42fefa39efp-1),
+ "0x0p+0",
+ "non-canonical form");
+
+ is(sprintf("%a", 0x0.2c5c85fdf473dep2 - 0x1.62e42fefa39efp-1),
+ "0x0p+0",
+ "non-canonical form");
+ }
+}
* multiple fp operations. */
bool hexfp = FALSE;
int total_bits = 0;
+ int significant_bits = 0;
#if NVSIZE == 8 && defined(HAS_QUAD) && defined(Uquad_t)
# define HEXFP_UQUAD
Uquad_t hexfp_uquad = 0;
#endif
NV hexfp_mult = 1.0;
UV high_non_zero = 0; /* highest digit */
+ int non_zero_integer_digits = 0;
PERL_ARGS_ASSERT_SCAN_NUM;
if (high_non_zero == 0 && b > 0)
high_non_zero = b;
+ if (high_non_zero)
+ non_zero_integer_digits++;
+
/* this could be hexfp, but peek ahead
* to avoid matching ".." */
if (UNLIKELY(HEXFP_PEEK(s))) {
* detection will shortly be more thorough with the
* underbar checks. */
const char* h = s;
+ significant_bits = non_zero_integer_digits * shift;
#ifdef HEXFP_UQUAD
hexfp_uquad = u;
#else /* HEXFP_NV */
hexfp_nv = u;
#endif
+ /* Ignore the leading zero bits of
+ * the high (first) non-zero digit. */
+ if (high_non_zero) {
+ if (high_non_zero < 0x8)
+ significant_bits--;
+ if (high_non_zero < 0x4)
+ significant_bits--;
+ if (high_non_zero < 0x2)
+ significant_bits--;
+ }
+
if (*h == '.') {
#ifdef HEXFP_NV
- NV mult = 1 / 16.0;
+ NV nv_mult = 1.0;
#endif
+ bool accumulate = TRUE;
for (h++; (isXDIGIT(*h) || *h == '_'); h++) {
if (isXDIGIT(*h)) {
U8 b = XDIGIT_VALUE(*h);
- total_bits += shift;
- if (total_bits < NV_MANT_DIG) {
+ significant_bits += shift;
#ifdef HEXFP_UQUAD
- hexfp_uquad <<= shift;
- hexfp_uquad |= b;
- hexfp_frac_bits += shift;
-#else /* HEXFP_NV */
- hexfp_nv += b * mult;
- mult /= 16.0;
-#endif
- } else if (total_bits - shift < NV_MANT_DIG) {
- /* A hexdigit straddling the edge of
- * mantissa. We can try grabbing as
- * many as possible bits. */
- int shift2 = 0;
- if (b & 1) {
- shift2 = 4;
- } else if (b & 2) {
- shift2 = 3;
- total_bits--;
- } else if (b & 4) {
- shift2 = 2;
- total_bits -= 2;
- } else if (b & 8) {
- shift2 = 1;
- total_bits -= 3;
+ if (accumulate) {
+ if (significant_bits < NV_MANT_DIG) {
+ /* We are in the long "run" of xdigits,
+ * accumulate the full four bits. */
+ hexfp_uquad <<= shift;
+ hexfp_uquad |= b;
+ hexfp_frac_bits += shift;
+ } else {
+ /* We are at a hexdigit either at,
+ * or straddling, the edge of mantissa.
+ * We will try grabbing as many as
+ * possible bits. */
+ int tail =
+ significant_bits - NV_MANT_DIG;
+ if (tail <= 0)
+ tail += shift;
+ hexfp_uquad <<= tail;
+ hexfp_uquad |= b >> (shift - tail);
+ hexfp_frac_bits += tail;
+
+ /* Ignore the trailing zero bits
+ * of the last non-zero xdigit.
+ *
+ * The assumption here is that if
+ * one has input of e.g. the xdigit
+ * eight (0x8), there is only one
+ * bit being input, not the full
+ * four bits. Conversely, if one
+ * specifies a zero xdigit, the
+ * assumption is that one really
+ * wants all those bits to be zero. */
+ if (b) {
+ if ((b & 0x1) == 0x0) {
+ significant_bits--;
+ if ((b & 0x2) == 0x0) {
+ significant_bits--;
+ if ((b & 0x4) == 0x0) {
+ significant_bits--;
+ }
+ }
+ }
+ }
+
+ accumulate = FALSE;
}
-#ifdef HEXFP_UQUAD
- hexfp_uquad <<= shift2;
- hexfp_uquad |= b;
- hexfp_frac_bits += shift2;
+ } else {
+ /* Keep skipping the xdigits, and
+ * accumulating the significant bits,
+ * but do not shift the uquad
+ * (which would catastrophically drop
+ * high-order bits) or accumulate the
+ * xdigits anymore. */
+ }
#else /* HEXFP_NV */
- PERL_UNUSED_VAR(shift2);
- hexfp_nv += b * mult;
- mult /= 16.0;
-#endif
+ if (accumulate) {
+ nv_mult /= 16.0;
+ if (nv_mult > 0.0)
+ hexfp_nv += b * nv_mult;
+ else
+ accumulate = FALSE;
}
+#endif
}
+ if (significant_bits >= NV_MANT_DIG)
+ accumulate = FALSE;
}
}
- if (total_bits >= 4) {
- if (high_non_zero < 0x8)
- total_bits--;
- if (high_non_zero < 0x4)
- total_bits--;
- if (high_non_zero < 0x2)
- total_bits--;
- }
-
- if (total_bits > 0 && (isALPHA_FOLD_EQ(*h, 'p'))) {
+ if ((total_bits > 0 || significant_bits > 0) &&
+ isALPHA_FOLD_EQ(*h, 'p')) {
bool negexp = FALSE;
h++;
if (*h == '+')
*d = '\0';
if (UNLIKELY(hexfp)) {
# ifdef NV_MANT_DIG
- if (total_bits > NV_MANT_DIG)
+ if (significant_bits > NV_MANT_DIG)
Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
"Hexadecimal float: mantissa overflow");
# endif