Normalize round() result to an exact int (#8511)

int.__round__ returned the receiver unchanged on the no-rounding path, so
round() on an int subclass handed back an instance of that subclass:
round(True) was True, not 1. CPython routes both no-op paths (ndigits is
None, and ndigits >= 0) through long_long(), which reuses the reference
only when the value is an exact int and otherwise copies it into a fresh
one.

Return zelf.__int__(vm).into_pyref(), which goes through into_exact_or()
and is the same normalization the neighbouring __trunc__, __floor__ and
__ceil__ already perform.

Assisted-by: Claude Code:claude-opus-5
This commit is contained in:
Jeongseop Lim authored and GitHub committed 2026-08-13 20:27:24 +09:00
1 parent 2a793fcdd1
commit 2e05364117
2 files changed
+21 -1

No files matched your search

+3 -1
View File
@@ -487,7 +487,9 @@ impl PyInt {
return vm.ctx.new_int(rounded);
}
}
zelf
// No rounding to do, but an int subclass must still be normalized to an
// exact int, the way CPython's long_long() does.
zelf.__int__(vm).into_pyref()
}
#[pymethod]
+18
View File
@@ -93,3 +93,21 @@ assert math.copysign(1.0, round(0.0, 2)) == 1.0
assert round(1.0, 1000) == 1.0
assert round(1.0, -1000) == 0.0
assert round(1.7976931348623157e308, 0) == 1.7976931348623157e308
# round() normalizes an int subclass to an exact int, like CPython's long_long().
assert round(True) == 1
assert type(round(True)) is int
assert type(round(True, 0)) is int
assert type(round(False)) is int
class MyInt(int):
pass
assert round(MyInt(5)) == 5
assert type(round(MyInt(5))) is int
assert type(round(MyInt(5), 2)) is int
# A negative ndigits already produced a fresh exact int.
assert type(round(MyInt(15), -1)) is int