stages_test: enable 22 more corpus files; upgrade decimal_float_fits_f64 to 128-bit
Upgrade decimal_float_fits_f64() to use 128-bit integer arithmetic for the algebraic round-trip test, handling mantissas up to ~38 significant digits. This fixes float128-vs-float tag mismatches for values that fit in 128-bit mantissa but overflow 64-bit. Newly enabled: gpu, memmove, mulf3, udivmodei4, udivmod, scalar, ff, p256_64, p256_scalar_64, p384_64, p384_scalar_64, secp256k1_64, secp256k1_scalar_64, sha2, sha3, Decompress, testing, log10, log2, log, rem_pio2f, rem_pio2_large, rem_pio2. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
130
stage0/astgen.c
130
stage0/astgen.c
@@ -3385,15 +3385,68 @@ static bool hex_float_fits_f64(const char* buf) {
|
||||
return total_sig_bits <= available_bits;
|
||||
}
|
||||
|
||||
// 128-bit multiply: (a_hi:a_lo) * b -> (r_hi:r_lo), returns true on overflow.
|
||||
static bool mul128x64(uint64_t a_hi, uint64_t a_lo, uint64_t b,
|
||||
uint64_t* r_hi, uint64_t* r_lo) {
|
||||
// Compute a_lo * b as 128-bit result using mul64.
|
||||
uint64_t lo_hi, lo_lo;
|
||||
mul64(a_lo, b, &lo_hi, &lo_lo);
|
||||
// Compute a_hi * b; if a_hi != 0 and result overflows 64 bits, overflow.
|
||||
uint64_t hi_prod = 0;
|
||||
if (a_hi != 0) {
|
||||
if (b != 0 && a_hi > UINT64_MAX / b)
|
||||
return true; // a_hi * b overflows uint64_t
|
||||
hi_prod = a_hi * b;
|
||||
}
|
||||
// Combine: result = hi_prod * 2^64 + lo_hi * 2^64 + lo_lo
|
||||
// = (hi_prod + lo_hi) * 2^64 + lo_lo
|
||||
uint64_t sum_hi = hi_prod + lo_hi;
|
||||
if (sum_hi < hi_prod)
|
||||
return true; // overflow
|
||||
*r_hi = sum_hi;
|
||||
*r_lo = lo_lo;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 128-bit divide: (a_hi:a_lo) / b -> (q_hi:q_lo), remainder in *rem.
|
||||
static void div128x64(uint64_t a_hi, uint64_t a_lo, uint64_t b,
|
||||
uint64_t* q_hi, uint64_t* q_lo, uint64_t* rem) {
|
||||
// High part division.
|
||||
*q_hi = a_hi / b;
|
||||
uint64_t r = a_hi % b;
|
||||
// Now divide (r * 2^64 + a_lo) by b, where r < b.
|
||||
// Bit-by-bit long division for the low 64-bit quotient.
|
||||
uint64_t quot = 0;
|
||||
uint64_t remainder = r;
|
||||
for (int i = 63; i >= 0; i--) {
|
||||
uint64_t bit = (a_lo >> i) & 1;
|
||||
bool will_overflow = (remainder >> 63) != 0;
|
||||
remainder = (remainder << 1) | bit;
|
||||
if (will_overflow || remainder >= b) {
|
||||
remainder -= b;
|
||||
quot |= (uint64_t)1 << i;
|
||||
}
|
||||
}
|
||||
*q_lo = quot;
|
||||
*rem = remainder;
|
||||
}
|
||||
|
||||
// Returns true if the decimal float string round-trips through f64.
|
||||
// A decimal value m * 10^e round-trips iff m * 5^e fits in 53 binary bits
|
||||
// (for e >= 0) or m is divisible by 5^|e| and the quotient fits in 53 bits
|
||||
// (for e < 0). Hex floats are handled separately by the caller.
|
||||
//
|
||||
// Upstream Zig parses as f128 and checks the f64 round-trip at f128 precision.
|
||||
// Since C (especially tcc) lacks f128 support, we use 128-bit integer
|
||||
// arithmetic for the algebraic round-trip test. This handles mantissas up to
|
||||
// ~38 significant digits. For the rare case of >38 digits (e.g. 2^128 written
|
||||
// in decimal), we fall back to a strtold heuristic.
|
||||
static bool decimal_float_fits_f64(const char* buf) {
|
||||
uint64_t m = 0;
|
||||
// Parse mantissa into 128-bit integer (m_hi:m_lo).
|
||||
uint64_t m_hi = 0, m_lo = 0;
|
||||
int e = 0;
|
||||
bool seen_dot = false;
|
||||
bool m_overflow = false;
|
||||
bool m_overflow = false; // true if mantissa overflows 128 bits
|
||||
const char* p = buf;
|
||||
|
||||
// Skip sign.
|
||||
@@ -3408,11 +3461,20 @@ static bool decimal_float_fits_f64(const char* buf) {
|
||||
}
|
||||
if (!m_overflow) {
|
||||
uint64_t digit = (uint64_t)(*p - '0');
|
||||
uint64_t next = m * 10 + digit;
|
||||
if (next / 10 != m) {
|
||||
uint64_t new_hi, new_lo;
|
||||
if (mul128x64(m_hi, m_lo, 10, &new_hi, &new_lo)) {
|
||||
m_overflow = true;
|
||||
} else {
|
||||
m = next;
|
||||
// Add digit.
|
||||
uint64_t sum_lo = new_lo + digit;
|
||||
uint64_t carry = (sum_lo < new_lo) ? 1 : 0;
|
||||
uint64_t sum_hi = new_hi + carry;
|
||||
if (sum_hi < new_hi) {
|
||||
m_overflow = true;
|
||||
} else {
|
||||
m_hi = sum_hi;
|
||||
m_lo = sum_lo;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (seen_dot)
|
||||
@@ -3439,35 +3501,33 @@ static bool decimal_float_fits_f64(const char* buf) {
|
||||
}
|
||||
|
||||
// Strip trailing zeros from mantissa.
|
||||
while (m > 0 && m % 10 == 0) {
|
||||
m /= 10;
|
||||
while (m_hi != 0 || m_lo != 0) {
|
||||
uint64_t q_hi, q_lo, rem;
|
||||
div128x64(m_hi, m_lo, 10, &q_hi, &q_lo, &rem);
|
||||
if (rem != 0)
|
||||
break;
|
||||
m_hi = q_hi;
|
||||
m_lo = q_lo;
|
||||
e++;
|
||||
}
|
||||
|
||||
if (m == 0)
|
||||
if (m_hi == 0 && m_lo == 0)
|
||||
return true;
|
||||
if (m_overflow) {
|
||||
// Mantissa overflows uint64_t (> 19 digits). We can't use the
|
||||
// exact algebraic test, so check the f64 value's significant bits.
|
||||
// Values with few significant bits (e.g. 2^113) are very likely
|
||||
// exact matches, confirmed by strtold. Values with many significant
|
||||
// bits (e.g. 5.999...e-01 with 21 digits) conservatively get f128.
|
||||
// Mantissa overflows 128-bit integer (> ~38 digits). This is rare.
|
||||
// Fall back to strtold heuristic for values like 2^128 in decimal.
|
||||
double d = strtod(buf, NULL);
|
||||
if (d == 0.0)
|
||||
return false; // non-zero value underflows in f64
|
||||
int exp;
|
||||
double frac = frexp(fabs(d), &exp);
|
||||
return false;
|
||||
int fexp;
|
||||
double frac = frexp(fabs(d), &fexp);
|
||||
uint64_t sig = (uint64_t)ldexp(frac, 53);
|
||||
// Count trailing zero bits in the f64 significand.
|
||||
uint32_t trailing = 0;
|
||||
uint64_t s = sig;
|
||||
while (s > 0 && (s & 1) == 0) {
|
||||
s >>= 1;
|
||||
trailing++;
|
||||
}
|
||||
// If the f64 value has ≤ 43 significant bits (≥ 10 trailing zeros),
|
||||
// the decimal string almost certainly represents this exact value.
|
||||
// Confirm with strtold comparison.
|
||||
if (trailing >= 10) {
|
||||
long double ld = strtold(buf, NULL);
|
||||
return ((long double)d == ld);
|
||||
@@ -3479,29 +3539,37 @@ static bool decimal_float_fits_f64(const char* buf) {
|
||||
// odd part of m contributes to the significand bit count. Powers of 2
|
||||
// just shift the binary exponent and don't affect whether the value
|
||||
// fits in f64's 53-bit significand.
|
||||
while (m > 0 && (m & 1) == 0)
|
||||
m >>= 1;
|
||||
while ((m_hi != 0 || m_lo != 0) && (m_lo & 1) == 0) {
|
||||
m_lo = (m_lo >> 1) | (m_hi << 63);
|
||||
m_hi >>= 1;
|
||||
}
|
||||
|
||||
if (e >= 0) {
|
||||
if (e > 23)
|
||||
return false;
|
||||
uint64_t hi, lo;
|
||||
mul64(m, pow5_table[e], &hi, &lo);
|
||||
if (hi != 0)
|
||||
uint64_t r_hi, r_lo;
|
||||
if (mul128x64(m_hi, m_lo, pow5_table[e], &r_hi, &r_lo))
|
||||
return false;
|
||||
return lo < ((uint64_t)1 << 53);
|
||||
if (r_hi != 0)
|
||||
return false;
|
||||
return r_lo < ((uint64_t)1 << 53);
|
||||
} else {
|
||||
int ae = -e;
|
||||
if (ae > 27)
|
||||
return false;
|
||||
uint64_t div = pow5_table[ae];
|
||||
if (m % div != 0)
|
||||
uint64_t q_hi, q_lo, rem;
|
||||
div128x64(m_hi, m_lo, div, &q_hi, &q_lo, &rem);
|
||||
if (rem != 0)
|
||||
return false;
|
||||
uint64_t quotient = m / div;
|
||||
// Factor out any remaining powers of 2 from the quotient.
|
||||
while (quotient > 0 && (quotient & 1) == 0)
|
||||
quotient >>= 1;
|
||||
return quotient < ((uint64_t)1 << 53);
|
||||
while ((q_hi != 0 || q_lo != 0) && (q_lo & 1) == 0) {
|
||||
q_lo = (q_lo >> 1) | (q_hi << 63);
|
||||
q_hi >>= 1;
|
||||
}
|
||||
if (q_hi != 0)
|
||||
return false;
|
||||
return q_lo < ((uint64_t)1 << 53);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -302,12 +302,12 @@ const corpus_files = .{
|
||||
"../lib/compiler_rt/int_from_float_test.zig",
|
||||
"../lib/compiler_rt/int_from_float.zig",
|
||||
"../lib/compiler_rt/int.zig",
|
||||
//"../lib/compiler_rt/log10.zig",
|
||||
//"../lib/compiler_rt/log2.zig",
|
||||
//"../lib/compiler_rt/log.zig",
|
||||
"../lib/compiler_rt/log10.zig",
|
||||
"../lib/compiler_rt/log2.zig",
|
||||
"../lib/compiler_rt/log.zig",
|
||||
"../lib/compiler_rt/memcmp.zig",
|
||||
"../lib/compiler_rt/memcpy.zig",
|
||||
//"../lib/compiler_rt/memmove.zig",
|
||||
"../lib/compiler_rt/memmove.zig",
|
||||
"../lib/compiler_rt/memset.zig",
|
||||
"../lib/compiler_rt/modti3_test.zig",
|
||||
"../lib/compiler_rt/modti3.zig",
|
||||
@@ -316,7 +316,7 @@ const corpus_files = .{
|
||||
"../lib/compiler_rt/muldc3.zig",
|
||||
"../lib/compiler_rt/muldf3.zig",
|
||||
"../lib/compiler_rt/mulf3_test.zig",
|
||||
//"../lib/compiler_rt/mulf3.zig",
|
||||
"../lib/compiler_rt/mulf3.zig",
|
||||
"../lib/compiler_rt/mulhc3.zig",
|
||||
"../lib/compiler_rt/mulhf3.zig",
|
||||
"../lib/compiler_rt/mulodi4_test.zig",
|
||||
@@ -356,9 +356,9 @@ const corpus_files = .{
|
||||
"../lib/compiler_rt/popcount.zig",
|
||||
"../lib/compiler_rt/powiXf2_test.zig",
|
||||
"../lib/compiler_rt/powiXf2.zig",
|
||||
//"../lib/compiler_rt/rem_pio2f.zig",
|
||||
//"../lib/compiler_rt/rem_pio2_large.zig",
|
||||
//"../lib/compiler_rt/rem_pio2.zig",
|
||||
"../lib/compiler_rt/rem_pio2f.zig",
|
||||
"../lib/compiler_rt/rem_pio2_large.zig",
|
||||
"../lib/compiler_rt/rem_pio2.zig",
|
||||
"../lib/compiler_rt/round.zig",
|
||||
"../lib/compiler_rt/shift_test.zig",
|
||||
"../lib/compiler_rt/shift.zig",
|
||||
@@ -397,11 +397,11 @@ const corpus_files = .{
|
||||
"../lib/compiler_rt/ucmpsi2_test.zig",
|
||||
"../lib/compiler_rt/ucmpti2_test.zig",
|
||||
"../lib/compiler_rt/udivmoddi4_test.zig",
|
||||
//"../lib/compiler_rt/udivmodei4.zig",
|
||||
"../lib/compiler_rt/udivmodei4.zig",
|
||||
"../lib/compiler_rt/udivmodsi4_test.zig",
|
||||
"../lib/compiler_rt/udivmodti4_test.zig",
|
||||
"../lib/compiler_rt/udivmodti4.zig",
|
||||
//"../lib/compiler_rt/udivmod.zig",
|
||||
"../lib/compiler_rt/udivmod.zig",
|
||||
"../lib/compiler_rt/udivti3.zig",
|
||||
"../lib/compiler_rt/umodti3.zig",
|
||||
"../lib/compiler_rt/unorddf2.zig",
|
||||
@@ -475,7 +475,7 @@ const corpus_files = .{
|
||||
"../lib/std/coff.zig",
|
||||
"../lib/std/compress/flate/BlockWriter.zig",
|
||||
"../lib/std/compress/flate/Compress.zig",
|
||||
//"../lib/std/compress/flate/Decompress.zig",
|
||||
"../lib/std/compress/flate/Decompress.zig",
|
||||
"../lib/std/compress/flate/HuffmanEncoder.zig",
|
||||
"../lib/std/compress/flate/Lookup.zig",
|
||||
"../lib/std/compress/flate/Token.zig",
|
||||
@@ -500,7 +500,7 @@ const corpus_files = .{
|
||||
"../lib/std/crypto/25519/edwards25519.zig",
|
||||
"../lib/std/crypto/25519/field.zig",
|
||||
"../lib/std/crypto/25519/ristretto255.zig",
|
||||
//"../lib/std/crypto/25519/scalar.zig",
|
||||
"../lib/std/crypto/25519/scalar.zig",
|
||||
"../lib/std/crypto/25519/x25519.zig",
|
||||
"../lib/std/crypto/aegis.zig",
|
||||
"../lib/std/crypto/aes/aesni.zig",
|
||||
@@ -531,7 +531,7 @@ const corpus_files = .{
|
||||
"../lib/std/crypto/codecs.zig",
|
||||
"../lib/std/crypto/ecdsa.zig",
|
||||
"../lib/std/crypto/errors.zig",
|
||||
//"../lib/std/crypto/ff.zig",
|
||||
"../lib/std/crypto/ff.zig",
|
||||
"../lib/std/crypto/ghash_polyval.zig",
|
||||
"../lib/std/crypto/hash_composition.zig",
|
||||
"../lib/std/crypto/hkdf.zig",
|
||||
@@ -544,19 +544,19 @@ const corpus_files = .{
|
||||
"../lib/std/crypto/pbkdf2.zig",
|
||||
"../lib/std/crypto/pcurves/common.zig",
|
||||
"../lib/std/crypto/pcurves/p256/field.zig",
|
||||
//"../lib/std/crypto/pcurves/p256/p256_64.zig",
|
||||
//"../lib/std/crypto/pcurves/p256/p256_scalar_64.zig",
|
||||
"../lib/std/crypto/pcurves/p256/p256_64.zig",
|
||||
"../lib/std/crypto/pcurves/p256/p256_scalar_64.zig",
|
||||
"../lib/std/crypto/pcurves/p256/scalar.zig",
|
||||
"../lib/std/crypto/pcurves/p256.zig",
|
||||
"../lib/std/crypto/pcurves/p384/field.zig",
|
||||
//"../lib/std/crypto/pcurves/p384/p384_64.zig",
|
||||
//"../lib/std/crypto/pcurves/p384/p384_scalar_64.zig",
|
||||
"../lib/std/crypto/pcurves/p384/p384_64.zig",
|
||||
"../lib/std/crypto/pcurves/p384/p384_scalar_64.zig",
|
||||
"../lib/std/crypto/pcurves/p384/scalar.zig",
|
||||
"../lib/std/crypto/pcurves/p384.zig",
|
||||
"../lib/std/crypto/pcurves/secp256k1/field.zig",
|
||||
"../lib/std/crypto/pcurves/secp256k1/scalar.zig",
|
||||
//"../lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig",
|
||||
//"../lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig",
|
||||
"../lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig",
|
||||
"../lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig",
|
||||
"../lib/std/crypto/pcurves/secp256k1.zig",
|
||||
"../lib/std/crypto/pcurves/tests/p256.zig",
|
||||
"../lib/std/crypto/pcurves/tests/p384.zig",
|
||||
@@ -566,8 +566,8 @@ const corpus_files = .{
|
||||
"../lib/std/crypto/salsa20.zig",
|
||||
"../lib/std/crypto/scrypt.zig",
|
||||
"../lib/std/crypto/Sha1.zig",
|
||||
//"../lib/std/crypto/sha2.zig",
|
||||
//"../lib/std/crypto/sha3.zig",
|
||||
"../lib/std/crypto/sha2.zig",
|
||||
"../lib/std/crypto/sha3.zig",
|
||||
"../lib/std/crypto/siphash.zig",
|
||||
"../lib/std/crypto/test.zig",
|
||||
"../lib/std/crypto/timing_safe.zig",
|
||||
@@ -623,7 +623,7 @@ const corpus_files = .{
|
||||
"../lib/std/fs/test.zig",
|
||||
"../lib/std/fs/wasi.zig",
|
||||
"../lib/std/fs.zig",
|
||||
//"../lib/std/gpu.zig",
|
||||
"../lib/std/gpu.zig",
|
||||
"../lib/std/hash/Adler32.zig",
|
||||
"../lib/std/hash/auto_hash.zig",
|
||||
"../lib/std/hash/benchmark.zig",
|
||||
@@ -902,7 +902,7 @@ const corpus_files = .{
|
||||
"../lib/std/tar/Writer.zig",
|
||||
"../lib/std/tar.zig",
|
||||
"../lib/std/testing/FailingAllocator.zig",
|
||||
//"../lib/std/testing.zig",
|
||||
"../lib/std/testing.zig",
|
||||
"../lib/std/Thread/Condition.zig",
|
||||
"../lib/std/Thread/Futex.zig",
|
||||
"../lib/std/Thread/Mutex/Recursive.zig",
|
||||
|
||||
Reference in New Issue
Block a user