Skip to content

Commit 93dc7c9

Browse files
committed
use np.ld.nmant + add SHA to sde url
1 parent c0e251d commit 93dc7c9

2 files changed

Lines changed: 71 additions & 29 deletions

File tree

.github/workflows/test_old_cpu.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ jobs:
3939

4040
- name: Install Intel SDE
4141
run: |
42-
curl -fSL -o /tmp/sde.tar.xz https://github.com/nihui/ncnn-assets/releases/download/toolchain/sde-external-10.8.0-2026-03-15-lin.tar.xz
42+
SDE_URL="https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz"
43+
SDE_SHA256="f849acecad4c9b108259c643b2688fd65c35723cd23368abe5dd64b917cc18c0"
44+
curl -o /tmp/sde.tar.xz "$SDE_URL"
45+
echo "$SDE_SHA256 /tmp/sde.tar.xz" | sha256sum -c -
4346
mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/
4447
sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde
4548

tests/test_quaddtype.py

Lines changed: 67 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4896,14 +4896,46 @@ def test_as_integer_ratio_compatibility_with_float(self, value):
48964896

48974897
BACKENDS = ["sleef", "longdouble"]
48984898

4899-
EXACT_BOTH_BACKENDS = [
4899+
LARGE_INTS = [
49004900
2**63 + 1, # 9223372036854775809, one past INT64_MAX, 64-bit span
49014901
-(2**63) - 1, # one past INT64_MIN
49024902
12345678901234567, # 17-digit int, ~2^53.4, needs 54 bits
49034903
18014398509481984001, # ~2^64, full 64-bit span
49044904
-18014398509481984001,
49054905
]
49064906

4907+
# Mantissa bits each backend can hold. SLEEF is always binary128. The longdouble
4908+
# backend is the platform's C long double: 80-bit (64 bits) on x86/x86-64, but only
4909+
# IEEE double (53 bits) on arm64 macOS / MSVC, and binary128 (113 bits) on some
4910+
# aarch64 Linux. np.longdouble is that same type, so finfo gives the right width.
4911+
_MANTISSA_BITS = {"sleef": 113, "longdouble": np.finfo(np.longdouble).nmant + 1}
4912+
4913+
4914+
def _round_to_significand(n, bits):
4915+
"""Round integer n to `bits` significant binary digits, round-half-to-even,
4916+
exactly as a binary float of that precision would store it. Pure integer math
4917+
so no float round-trip can taint the reference value."""
4918+
if n == 0:
4919+
return 0
4920+
neg = n < 0
4921+
n = abs(n)
4922+
shift = n.bit_length() - bits
4923+
if shift <= 0:
4924+
return -n if neg else n
4925+
lower = n & ((1 << shift) - 1)
4926+
half = 1 << (shift - 1)
4927+
r = n >> shift
4928+
if lower > half or (lower == half and (r & 1)):
4929+
r += 1
4930+
r <<= shift
4931+
return -r if neg else r
4932+
4933+
4934+
def expected_int(n, backend):
4935+
"""The exact integer the given backend actually stores for integer n: n itself
4936+
where the backend's mantissa is wide enough, else the correctly-rounded value."""
4937+
return _round_to_significand(n, _MANTISSA_BITS[backend])
4938+
49074939

49084940
class TestIntConversion:
49094941
"""Regression tests for issue #97: int(QuadPrecision(...)) must
@@ -4949,39 +4981,42 @@ def test_int_truncates_toward_zero(self, backend, value_str, expected):
49494981
assert int(QuadPrecision(value_str, backend=backend)) == int(float(value_str))
49504982

49514983
@pytest.mark.parametrize("backend", BACKENDS)
4952-
@pytest.mark.parametrize("n", EXACT_BOTH_BACKENDS)
4984+
@pytest.mark.parametrize("n", LARGE_INTS)
49534985
def test_int_exact_when_double_would_lose_precision(self, backend, n):
4954-
assert int(QuadPrecision(str(n), backend=backend)) == n
4986+
# >53 bits: routing the longdouble backend through double (the original
4987+
# bug) returned the rounded neighbour even where long double is wider.
4988+
# int() must be as precise as the backend's own float type, never worse.
4989+
assert int(QuadPrecision(str(n), backend=backend)) == expected_int(n, backend)
49554990

49564991
# ---- Beyond int64: the original bug ----
49574992

49584993
@pytest.mark.parametrize("backend", BACKENDS)
49594994
def test_int_beyond_int64_positive(self, backend):
49604995
# 2^63 = 9223372036854775808 — one past INT64_MAX. The old code returned
4961-
# INT64_MAX (9223372036854775807). Must now be exact.
4996+
# INT64_MAX (9223372036854775807). 2^63 is a power of two: exact everywhere.
49624997
n = 2**63
49634998
assert int(QuadPrecision(str(n), backend=backend)) == n
49644999

49655000
@pytest.mark.parametrize("backend", BACKENDS)
49665001
def test_int_beyond_int64_negative(self, backend):
49675002
n = -(2**63) - 1 # one past INT64_MIN
4968-
assert int(QuadPrecision(str(n), backend=backend)) == n
5003+
assert int(QuadPrecision(str(n), backend=backend)) == expected_int(n, backend)
49695004

49705005
@pytest.mark.parametrize("backend", BACKENDS)
49715006
@pytest.mark.parametrize("exponent", [40, 60, 80, 100])
49725007
def test_int_powers_of_two_far_above_int64(self, backend, exponent):
4973-
# Powers of two are exact in both the 64-bit and 113-bit mantissas.
5008+
# Powers of two have a 1-bit mantissa: exact in every float format.
49745009
n = 2 ** exponent
49755010
assert int(QuadPrecision(str(n), backend=backend)) == n
49765011

49775012
@pytest.mark.parametrize("backend", BACKENDS)
49785013
def test_int_int64_max_exact(self, backend):
49795014
m = 2**63 - 1
4980-
assert int(QuadPrecision(str(m), backend=backend)) == m
5015+
assert int(QuadPrecision(str(m), backend=backend)) == expected_int(m, backend)
49815016

49825017
@pytest.mark.parametrize("backend", BACKENDS)
49835018
def test_int_int64_min_exact(self, backend):
4984-
m = -(2**63)
5019+
m = -(2**63) # power of two: exact everywhere
49855020
assert int(QuadPrecision(str(m), backend=backend)) == m
49865021

49875022
# ---- 1e30 from the issue ----
@@ -4994,21 +5029,21 @@ def test_int_1e30_sleef_exact(self):
49945029

49955030
@pytest.mark.parametrize("backend", BACKENDS)
49965031
def test_int_1e30_not_saturated(self, backend):
4997-
# Both backends must return a ~1e30 magnitude integer, never the old
4998-
# INT64_MAX saturation. (longdouble rounds 1e30; we only check it is the
4999-
# correctly-rounded huge value, within long double's ~19-digit precision.)
5032+
# The original bug saturated at INT64_MAX. Both backends must return a
5033+
# ~1e30 magnitude integer: the value the backend's float type rounds to.
50005034
result = int(QuadPrecision("1e30", backend=backend))
50015035
assert result > 2**64
5002-
assert abs(result - 10**30) < 10**(30 - 17)
5036+
assert result == expected_int(10**30, backend)
50035037

50045038
@pytest.mark.parametrize("backend", BACKENDS)
50055039
def test_int_huge_value_not_truncated(self, backend):
5006-
# int(1e1000) needs ~1001 digits. The old fixed 128-byte buffer truncated
5007-
# to 127 garbage digits with no error. Must now return the full exact
5008-
# integer of the nearest representable value.
5009-
result = int(QuadPrecision("1e1000", backend=backend))
5010-
assert len(str(result)) > 900
5011-
assert abs(result - 10**1000) < 10**(1000 - 17)
5040+
# The old fixed 128-byte buffer truncated huge values to ~127 garbage
5041+
# digits with no error. Use a value huge yet finite in the backend's float
5042+
# type: binary128 holds 1e1000 (~1001 digits); long double tops out near
5043+
# 1e308 where it is only IEEE double, so use 1e300 (~301 digits) there.
5044+
value_str = "1e1000" if backend == "sleef" else "1e300"
5045+
result = int(QuadPrecision(value_str, backend=backend))
5046+
assert len(str(result)) > 250 # far beyond the old ~127-char buffer
50125047

50135048
# ---- Return type ----
50145049

@@ -5031,7 +5066,7 @@ def test_int_of_huge_value_returns_python_int(self, backend):
50315066
-(2**63), -(2**63) - 1, -(2**70),
50325067
])
50335068
def test_int_quad_int_roundtrip(self, backend, n):
5034-
assert int(QuadPrecision(str(n), backend=backend)) == n
5069+
assert int(QuadPrecision(str(n), backend=backend)) == expected_int(n, backend)
50355070

50365071

50375072
class TestLongdoubleBackendExactness:
@@ -5040,8 +5075,10 @@ class TestLongdoubleBackendExactness:
50405075
with the exact value on BOTH backends for integers needing >53 mantissa bits."""
50415076

50425077
@pytest.mark.parametrize("backend", BACKENDS)
5043-
@pytest.mark.parametrize("n", EXACT_BOTH_BACKENDS)
5078+
@pytest.mark.parametrize("n", LARGE_INTS)
50445079
def test_is_integer_true_for_large_exact_ints(self, backend, n):
5080+
# Rounding a large int to the backend's float still yields an integer, so
5081+
# is_integer() is True on both backends regardless of mantissa width.
50455082
assert QuadPrecision(str(n), backend=backend).is_integer() is True
50465083

50475084
@pytest.mark.parametrize("backend", BACKENDS)
@@ -5050,19 +5087,21 @@ def test_is_integer_false_for_non_integers(self, backend, value_str):
50505087
assert QuadPrecision(value_str, backend=backend).is_integer() is False
50515088

50525089
@pytest.mark.parametrize("backend", BACKENDS)
5053-
@pytest.mark.parametrize("n", EXACT_BOTH_BACKENDS)
5090+
@pytest.mark.parametrize("n", LARGE_INTS)
50545091
def test_as_integer_ratio_reconstructs_large_exact_ints(self, backend, n):
5055-
# For an integer value the ratio is exactly n/1; n == num/den must hold
5056-
# exactly even without assuming the impl reduces the fraction.
5092+
# For an integer-valued scalar the ratio is exactly v/1, where v is the
5093+
# value the backend actually stores. num == v * den must hold exactly.
5094+
v = expected_int(n, backend)
50575095
num, den = QuadPrecision(str(n), backend=backend).as_integer_ratio()
5058-
assert num == n * den
5096+
assert num == v * den
50595097

50605098
@pytest.mark.parametrize("backend", BACKENDS)
5061-
@pytest.mark.parametrize("n", EXACT_BOTH_BACKENDS + [0, 1, -1, 42])
5099+
@pytest.mark.parametrize("n", LARGE_INTS + [0, 1, -1, 42])
50625100
def test_hash_matches_python_int(self, backend, n):
5063-
# Key invariant: hash(QuadPrecision(n)) == hash(n) for integer-valued n,
5064-
# so a quad scalar and the equal Python int collide in dict/set lookups.
5065-
assert hash(QuadPrecision(str(n), backend=backend)) == hash(n)
5101+
# hash(QuadPrecision(n)) == hash(v), where v is the value the backend
5102+
# stores, so a quad scalar and the equal Python int collide in dict/sets.
5103+
v = expected_int(n, backend)
5104+
assert hash(QuadPrecision(str(n), backend=backend)) == hash(v)
50665105

50675106

50685107
def test_quadprecision_scalar_dtype_expose():

0 commit comments

Comments
 (0)