Measured 2026-09-10, compiler pascal26 at HEAD
The three rows that name the defect
var d: Double; v, w: Variant;
d := 0.0; writeln(bits(-d)); { 8000000000000000 correct }
v := 0.0; w := -v; writeln(bits(w)); { 0000000000000000 WRONG }
w := pyneg_v(v); writeln(...); { 8000000000000000 correct }
One program, one run. pyneg_v in compiler/builtin/pylib.pas:9834 does the
right thing — it tests PyVarIsFloat and writes -PyVarAsFloat(p) — and the
compiler is not routing -v to it. 0.0 - 0.0 is +0.0 in IEEE 754, which is
exactly the answer observed, so the lowering is a subtraction.
This is the half-wired-door shape: the door exists, it is correct, and the committed path does not use it. It does not fail by refusing; it fails by answering.
The NilPy split, which is the same fact seen from the frontend
| expression | result |
|---|---|
-v for a local v = 0.0 |
correct, -0.0 |
-(0.0) literal |
correct |
-c.f for a declared float attribute |
correct |
-a in def g(a: float) |
correct |
-a in def h(a) — untyped |
wrong, +0.0 |
-lst[0] |
wrong |
-tup[0] |
wrong |
-dct["k"] |
wrong |
The typed/untyped parameter pair is the control: same syntax, same value, and the only thing that differs is whether the operand is a Variant.
Why it is not prio 20
Signed zero is not decoration in the one place it is most likely to be read
through a Variant. atan2 sends +0 and -0 to opposite sides of the axis —
lib/rtl/math.pas's ArcTan2 has a comment about exactly that, and its own
signed-zero rows are asserted — so math.atan2(-0.0, 1.0) is -0.0 in CPython
and +0.0 here as soon as the argument reaches the call through a list, a
tuple, a dict or an untyped parameter, which is how a Python program normally
holds a number. copysign, 1/x and any sign-of-zero test inherit it too.
It stays at 35 rather than higher because a program that CARES about the sign of zero and holds the value in a Variant is rare, and no corpus site is known to hit it.
How it was found
Building test/test_nilpy_math_atan_and_atan2_bit_for_bit.npy
([[bug-b-arctan-answers-nan-above-1e300-which-is-why-math-atan2-is-still-refused]]).
A row wrote math.atan(-x) while looping for x in xs, and the x = 0.0
iteration disagreed with CPython. The test now writes every sign out longhand
and records the absence in its header rather than asserting a known divergence
against a CPython-generated oracle.