Skip to content

Commit 4e2ef0c

Browse files
physicsrobclaude
andcommitted
ops: thread sharpness through ceil_int to its inner floor_int
ceil_int(x) = -floor_int(-x); floor_int already takes a sharpness override but ceil_int did not forward it, pinning ceil to the default 0.1-wide ramp. The DOOM screen-y staircase (CEIL_Y) needs sharpness=10000 (1e-4 ramp) so a raw screen-y carrying ~0.077 of piecewise-linear multiply noise stays in the flat zone and ceils to an exact scanline integer. Additive kwarg; the default (None) path is byte-identical, so the measured noise footer is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 57ef38c commit 4e2ef0c

2 files changed

Lines changed: 41 additions & 2 deletions

File tree

tests/ops/test_arithmetic_ops.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,32 @@ def test_ceil_int_wide_range_large_magnitude():
941941
assert abs(r - math.ceil(v)) < 0.01, f"ceil_int({v}) should be {math.ceil(v)}, got {r}"
942942

943943

944+
def test_ceil_int_high_sharpness_screen_y():
945+
"""ceil_int must forward sharpness to its inner floor_int (the DOOM screen-y
946+
CEIL_Y staircase). At sharpness=10000 the ramp is 1e-4 wide, so a raw screen-y
947+
carrying ~0.077 of piecewise-linear multiply noise stays in the flat zone and
948+
ceils to an exact integer instead of interpolating across a scanline boundary.
949+
Covers the narrow [0,49] and wide [0,128] / [-128,49] ranges H actually uses."""
950+
import math
951+
952+
x = create_input("x", 1)
953+
# Mid-bin offsets (well outside the 1e-4 ramp) over the screen-y ranges.
954+
ranges = [(0, 49), (0, 128), (-128, 49)]
955+
offsets = (0.077, 0.4, -0.077, -0.4, 0.953)
956+
for lo, hi in ranges:
957+
c = ceil_int(x, min_value=lo, max_value=hi, sharpness=10000.0)
958+
for k in range(lo, hi):
959+
for off in offsets:
960+
v = k + off
961+
if not (lo <= v <= hi):
962+
continue
963+
r = c.compute(n_pos=1, input_values={"x": torch.tensor([[v]])}).item()
964+
assert abs(r - math.ceil(v)) < 0.01, (
965+
f"ceil_int({v}, [{lo},{hi}], s=10000) should be "
966+
f"{math.ceil(v)}, got {r}"
967+
)
968+
969+
944970
def test_signed_multiply():
945971
"""Test all sign combinations."""
946972
a = create_input("a", 1)

torchwright/ops/arithmetic_ops.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2361,13 +2361,24 @@ def floor_int(
23612361
)
23622362

23632363

2364-
def ceil_int(inp: Node, min_value: int, max_value: int) -> Node:
2364+
def ceil_int(
2365+
inp: Node,
2366+
min_value: int,
2367+
max_value: int,
2368+
sharpness: Optional[float] = None,
2369+
) -> Node:
23652370
"""Compute ceil(x) using the identity ``ceil(x) = -floor(-x)``.
23662371
23672372
Args:
23682373
inp: 1D scalar node with value in [min_value, max_value].
23692374
min_value: Lower bound (integer).
23702375
max_value: Upper bound (integer).
2376+
sharpness: Override the global ``step_sharpness`` for the inner
2377+
``floor_int``. Higher values narrow the ramp zone at each
2378+
integer boundary (e.g. 10000 gives a 1e-4-wide ramp vs. the
2379+
default 10 which gives 0.1) so inputs carrying a few tenths of
2380+
piecewise-linear noise stay in the flat zone and ceil exactly.
2381+
Mirrors :func:`floor_int`'s ``sharpness``.
23712382
23722383
Returns:
23732384
1D scalar node containing ceil(x).
@@ -2378,7 +2389,9 @@ def ceil_int(inp: Node, min_value: int, max_value: int) -> Node:
23782389
measured at commit 2e04d93. See docs/numerical_noise.md.
23792390
"""
23802391
assert len(inp) == 1, "Input must be a 1D scalar node"
2381-
return negate(floor_int(negate(inp), -max_value, -min_value))
2392+
return negate(
2393+
floor_int(negate(inp), -max_value, -min_value, sharpness=sharpness)
2394+
)
23822395

23832396

23842397
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)