← board

The inverse trig functions have no fast arm and cost ~16 microseconds a call

Sin, Cos and Tan in lib/rtl/math.pas each carry two arms:

{$ifdef PXX_FLOAT_EXACT}
  SinCosDd(Abs(x), sn, cs);      { ~106 bits }
{$else}
  SinCosFast(Abs(x), sc);        { the default }
{$endif}

ArcTan (1656), ArcSin (1693), ArcCos (1718) and ArcTan2 (1759) have zero PXX_FLOAT_EXACT guards. They are always double-double.

PXX_FLOAT_EXACT is defined in exactly one place in the tree — a Makefile line for test/lib_math_correctly_rounded.pas — so SinCosDd is unreachable in any normal build, while DdAtan is on the default path for every ArcTan2 call.

Why it costs what it does

DdAtan runs up to six half-angle steps, each with a DdSqrt and a DdDiv, then a 13-term series where every term is four double-double operations, then the doublings back. Roughly 50–60 non-inlined record-returning calls.

Profile of 400k ArcTan calls, 120 samples — no single pathology, it is volume:

Dd2Prod 25.2%   DdAtan 18.7%   DdFast2Sum 15.9%   Dd2Sum 10.3%
DdMulD 7.5%   DdDivD 6.5%   DdMul 5.6%   DdAdd 4.7%

A separate datum for Track O, measured here and not chased

Same binary, same 400k calls, three interleaved reps, min:

-O0 5.77s    -O2 5.58s    -O3 3.93s

-O2 buys 3.3% on this code and -O3 buys 31.9%. Whatever -O3 is doing for dd-heavy record-returning code, -O2 is not. Recorded because it was measured, not because it has been investigated — it may be one pass, and it may be specific to this shape.

What the fix must not do

Do not delete the exact arm. test/lib_math_correctly_rounded.pas builds with -dPXX_FLOAT_EXACT and asserts the last bit; the port to double-double exists because the previous plain-double ArcTan disagreed with libm on 2065 of 3005 random arguments, up to 4 ulp. The fast arm must be a second path, not a replacement, and test/lib_math_fast_tolerance.pas is where its tolerance is asserted.

The bit-for-bit assertion is singular, and that is what makes the fork narrow

Measured 2026-09-22, independently by frankuser and re-verified here against the tree. Population: the 8 test_nilpy_math_*.npy files.

count held to
with a stored .expected 5 a frozen copy of OUR OWN output
without one (live CPython diff) 3 CPython's live bits

The three without are atan_and_atan2_bit_for_bit, fabs_os_basename and floor_ceil_int. The latter two touch only exact operations — the complete set of math. calls in both files is ceil, copysign, fabs, floor, trunc, where bit-for-bit is free and carries no contract.

So atan/atan2 is the only transcendental anywhere in the suite held to CPython's live bits. Every other one — acos asin log log10 log2 pow sqrt degrees radians frexp fsum modf isqrt — is held to a frozen copy of our own output. And math.sin / math.cos / math.tan are referenced by no nilpy math test at all, despite having had fast arms for a long time.

Corroborating measurement, mine: sin/cos already differ from CPython on 51 of 273 rows (18.7%) in the tree as it stands, with nothing of mine in it. A sibling test_nilpy_math_sin_bit_for_bit.npy written today would be RED on arrival and would have been for months.

So the file has been asserting "this function has not been optimised yet" rather than "this function is exact" (7a's phrasing). Its header explains the regenerating oracle as a defence against staleness, which is a good reason that carried a contract as a side effect nobody chose.

The fork, for whoever settles it: is bit-for-bit with CPython the contract for transcendentals, given it is already false for sin/cos and has been for some time? If it was never the intent, this red is a test that was passing for the wrong reason. If it IS the intent, sin/cos are 51 rows out of contract today and that is the larger defect. Deliberately no fix pre-registered here.