Repository navigation
test(kernel): cover the 32-bit rounding path and round_py vs CPython - #41
Merged
Merged
Conversation
Three gaps in the kernel's CI coverage, all exposed by the round_py rewrite in #39. The 32-bit arithmetic path was never executed. __int128 is a 64-bit-target extension, so the i686 and armv7l builds scale through uint64_t instead, but the runner is x86_64 and the cross-built 32-bit binaries are never run - so that path shipped with no coverage at all. build_kernel.sh now honours EXTRA_CXXFLAGS and CI builds a -DPK_NO_INT128 variant, which forces the narrow path on a 64-bit host, then puts it through the full parity suite. The binaries that actually ship were the ones nobody verified. The pre-commit job checks the checked-in x86_64 binary as it stood when the PR was opened - the previous one - and the kernel-binaries job then regenerates all six and pushes them. That push's workflow run needs manual approval, so without it the regenerated binaries merge unverified; this happened on #39 and was only caught by approving the run by hand. Verification now happens inside kernel-binaries, after the cross-build and before the commit. round_py itself had no test. Parity was only ever checked end to end through the Python engine over realistic SoC magnitudes, which is why the old snprintf("%.*f") implementation shipped for so long with a 64-byte buffer: 1e300 needs 305 characters, so it truncated and strtod parsed a different number - round(1e300, 3) returned 1e62 and nothing noticed. A pk_round_py_test hook now exposes round_py so tests/test_round_py_parity.py can diff it bit-for-bit against CPython round() over the inputs that break naive implementations: exact ties at j/2^p, nextafter neighbours either side of a decimal boundary, halfway values at 4dp, subnormals and extreme magnitudes. Verified the new test actually catches the original defect: rebuilt the kernel with the pre-#39 round_py and it reports 80 mismatches, all on the huge-value cases, and passes on the current implementation. 19776 values checked in 0.03s, on both the __int128 and narrow builds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
cspell applies a different dictionary per language, so ndigits passes in prediction_kernel.cpp - the C++ dictionary knows it - but not in the new Python test. CXXFLAGS is new to the repo entirely. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three gaps in the kernel's CI coverage, all exposed while doing the
round_pyrewrite in #39. None of these are repairs to something broken — CI has been green throughout — they are coverage for code that currently ships untested.1. The 32-bit arithmetic path was never executed
__int128is a 64-bit-target extension, so thei686andarmv7lbuilds scale throughuint64_tinstead. But the runner is x86_64 and the cross-built 32-bit binaries are never run, so that path shipped with no coverage at all.build_kernel.shnow honoursEXTRA_CXXFLAGS, and CI builds a-DPK_NO_INT128variant — which forces the narrow path on a 64-bit host — then puts it through the full parity suite.2. The binaries that actually ship were the ones nobody verified
The ordering was wrong:
pre-commitverifies the checked-in x86_64 binary — as it stood when the PR was opened, i.e. the previous onekernel-binariesregenerates all six and pushes themSo without intervention the regenerated binaries merge unverified. This happened on #39 and was only caught because I approved the pending run by hand. Verification now happens inside
kernel-binaries, after the cross-build and before the commit.x86_64 is the only one the runner can execute natively; the others rely on the source-level parity coverage in
pre-commit(now including the narrow path, per #1).3.
round_pyhad no testParity was only ever checked end to end through the Python engine, over realistic SoC magnitudes. That is precisely why the old
snprintf("%.*f")implementation shipped for so long with a 64-byte buffer:1e300needs 305 characters, so it truncated andstrtodparsed a completely different number —round(1e300, 3)returned1e62, and nothing noticed.A
pk_round_py_testhook exposesround_pysotests/test_round_py_parity.pycan diff it bit-for-bit against CPython'sround()over the inputs that break naive implementations: exact ties atj/2^p,nextafterneighbours either side of a decimal boundary, halfway values at 4dp, subnormals, and extreme magnitudes.Verification
The new test demonstrably catches the original defect. Rebuilding the kernel with the pre-#39
round_py:All 80 on the huge-value cases. Against the current implementation it passes — 19,776 values in 0.03s, on both the
__int128and narrow builds.Also run locally:
round_py_parity,kernel_parity(450 randomised configs) andmodel_kernelall pass.🤖 Generated with Claude Code