mirror of
https://github.com/allaunthefox/SilverSight.git
synced 2026-07-31 01:25:21 +00:00
fix(python): q16_mul/q16_div pure integer toward-zero truncation
Replaced float division + banker's rounding with pure integer division truncating toward zero, matching FixedPoint.lean:298 and FixedPoint.lean:302 exactly. Added _div_toward_zero helper for Python's // vs Lean Int / semantics mismatch. Updated docstring: 'CANONICAL ROUNDING MODE' → 'ARITHMETIC MODE: truncation toward zero matches Lean Int /'. No float in compute paths. float_to_q16 (I/O boundary) unchanged.
This commit is contained in:
parent
c3a5e6b60b
commit
68844c92c4
1 changed files with 33 additions and 30 deletions
|
|
@ -1,15 +1,16 @@
|
|||
"""Canonical Q16_16 fixed-point arithmetic.
|
||||
|
||||
Single source of truth: CoreFormalism/Q16_16_Spec.lean
|
||||
Single source of truth: Core/SilverSight/FixedPoint.lean
|
||||
All operations MUST produce identical results to the Lean implementation.
|
||||
|
||||
Q16_16 represents fixed-point numbers with 16 integer bits and 16 fractional bits.
|
||||
Range: [-32768.0, 32767.9999847412109375]
|
||||
Resolution: 1/65536 ≈ 0.0000152587890625
|
||||
|
||||
CANONICAL ROUNDING MODE: round-half-up (banker's rounding)
|
||||
- Values exactly at half-LSB round to nearest even
|
||||
- All other values round to nearest
|
||||
ARITHMETIC MODE: truncation toward zero (matches Lean Int / division)
|
||||
- Lean mul (FixedPoint.lean:298): (a.toInt * b.toInt) / q16Scale
|
||||
- Lean div (FixedPoint.lean:302): (a.toInt * q16Scale) / b.toInt
|
||||
- No rounding: Q16_16 is a quotient type with floor semantics
|
||||
"""
|
||||
|
||||
import math
|
||||
|
|
@ -126,28 +127,34 @@ def q16_sub(a: int, b: int) -> int:
|
|||
return max(Q16_MIN_RAW, min(Q16_MAX_RAW, result))
|
||||
|
||||
|
||||
def q16_mul(a: int, b: int) -> int:
|
||||
"""Multiply two Q16_16 values with canonical rounding.
|
||||
def _div_toward_zero(num: int, den: int) -> int:
|
||||
"""Integer division truncating toward zero.
|
||||
|
||||
result = canonical_round((a * b) / 65536)
|
||||
|
||||
Uses 64-bit intermediate, then applies banker's rounding.
|
||||
Python's // floors toward negative infinity.
|
||||
Lean's Int / truncates toward zero.
|
||||
This helper matches Lean's semantics.
|
||||
"""
|
||||
# Use Python's arbitrary precision integers (no overflow issue)
|
||||
prod_64 = a * b
|
||||
if num >= 0:
|
||||
return num // den
|
||||
else:
|
||||
return -(-num // den)
|
||||
|
||||
# Divide by scale with banker's rounding
|
||||
# prod_64 / 65536 with half-to-even
|
||||
scaled = prod_64 / Q16_SCALE # This is a float division for correct rounding
|
||||
rounded = round(scaled) # Banker's rounding
|
||||
|
||||
return max(Q16_MIN_RAW, min(Q16_MAX_RAW, rounded))
|
||||
def q16_mul(a: int, b: int) -> int:
|
||||
"""Multiply two Q16_16 values with truncation toward zero.
|
||||
|
||||
Matches FixedPoint.lean:298: (a.toInt * b.toInt) / q16Scale
|
||||
|
||||
Uses Python's arbitrary precision integers (no overflow).
|
||||
"""
|
||||
prod = a * b
|
||||
return max(Q16_MIN_RAW, min(Q16_MAX_RAW, _div_toward_zero(prod, Q16_SCALE)))
|
||||
|
||||
|
||||
def q16_div(a: int, b: int) -> int:
|
||||
"""Divide two Q16_16 values with canonical rounding.
|
||||
"""Divide two Q16_16 values with truncation toward zero.
|
||||
|
||||
result = canonical_round((a * 65536) / b)
|
||||
Matches FixedPoint.lean:302: (a.toInt * q16Scale) / b.toInt
|
||||
|
||||
Args:
|
||||
a: Dividend (Q16_16 raw value)
|
||||
|
|
@ -159,12 +166,8 @@ def q16_div(a: int, b: int) -> int:
|
|||
if b == 0:
|
||||
raise ZeroDivisionError("Q16_16 division by zero")
|
||||
|
||||
# (a * 65536) / b with banker's rounding
|
||||
num = a * Q16_SCALE
|
||||
scaled = num / b # Float division for correct rounding
|
||||
rounded = round(scaled)
|
||||
|
||||
return max(Q16_MIN_RAW, min(Q16_MAX_RAW, rounded))
|
||||
return max(Q16_MIN_RAW, min(Q16_MAX_RAW, _div_toward_zero(num, b)))
|
||||
|
||||
|
||||
# ============================================================
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue