TorchLean API

FloatLib.Numerics.ShiftRightJam.Proof

Nearest-even rounding through guard and sticky bits #

The width-generic rounder Numerics.roundShiftRightEven compares the discarded remainder with the halfway value 2^(shift - 1). Limb kernels can avoid constructing that value by using three Boolean tests: the guard bit just below the kept quotient, whether any lower bit is set, and the parity of the quotient. This module proves that restatement and its consequences for the sticky-bit normalisation used by addition and fused multiply-add.

These statements apply to natural numbers independently of their storage. The fixed-word and limb-array kernels use them to justify their rounding steps.

The guard-and-sticky form #

The number one has no bits above position zero.

theorem FloatLib.Numerics.mod_two_pow_succ_eq (x t : ) :
x % 2 ^ (t + 1) = x % 2 ^ t + 2 ^ t * if x.testBit t = true then 1 else 0

The remainder of a shift splits into the sticky bits and the guard bit.

theorem FloatLib.Numerics.roundShiftRightEven_eq_guard_sticky (x s : ) (hs : 0 < s) :
roundShiftRightEven x s = x / 2 ^ s + if (x.testBit (s - 1) && (decide (x % 2 ^ (s - 1) 0) || x.testBit s)) = true then 1 else 0

Nearest-even rounding in guard-and-sticky form.

The quotient is incremented exactly when the guard bit is set and either a sticky bit is set or the quotient is odd.

The nearest-even quotient is at most the successor of the truncated quotient.

The nearest-even quotient is at least the truncated quotient.

Jamming #

theorem FloatLib.Numerics.or_one_eq (q : ) :
q ||| 1 = if q % 2 = 0 then q + 1 else q

Setting the low bit of a natural number.

Jamming never decreases the truncated quotient.

Jamming a value with a set bit at or above the jam position gives a nonzero result.

Rounding after a sticky-bit jam agrees with rounding the original value, provided the rounding keeps at least the guard bit above the jammed position.

theorem FloatLib.Numerics.shiftRightJam_add_mul_two_pow (A B j : ) :
shiftRightJam (A * 2 ^ j + B) j = if B % 2 ^ j = 0 then A + B / 2 ^ j else A + B / 2 ^ j ||| 1

The jammed quotient of an aligned sum.

theorem FloatLib.Numerics.shiftRightJam_mul_two_pow_sub (A B j : ) (hB : B A * 2 ^ j) :
shiftRightJam (A * 2 ^ j - B) j = if B % 2 ^ j = 0 then A - B / 2 ^ j else A - B / 2 ^ j - 1 ||| 1

The jammed quotient of an aligned difference.

theorem FloatLib.Numerics.log2_shiftRightJam (x j : ) (hx : 2 ^ (j + 1) x) :

Jamming preserves the shifted leading-bit position when the truncated quotient is at least two.

Scaling #

Rounding a value scaled by 2^k by k more bits is rounding the value.

theorem FloatLib.Numerics.roundShiftRightEven_mul_two_pow_of_le (x k s : ) (hs : s k) :
roundShiftRightEven (x * 2 ^ k) s = x * 2 ^ (k - s)

Rounding a value scaled by 2^k by at most k bits is exact.