Fix scoreboard remainder overflow for large divisors

This commit is contained in:
Florian ERNST committed 2026-11-02 14:16:12 +01:00
1 parent 993eabe86b
commit 52453208cd
3 files changed
+62 -1

No files matched your search

+8 -1
View File
@@ -797,7 +797,14 @@ pub fn apply_operation(op: &str, a: i32, b: i32) -> CmdResult<(i32, i32)> {
if b == 0 {
return Err(div0());
}
(a.wrapping_rem(b).wrapping_add(b).wrapping_rem(b), b)
let remainder = a.wrapping_rem(b);
// Floor remainder has the divisor's sign; only correct opposite signs.
let remainder = if remainder != 0 && ((remainder < 0) != (b < 0)) {
remainder + b
} else {
remainder
};
(remainder, b)
}
"<" => (a.min(b), b),
">" => (a.max(b), b),
@@ -210,6 +210,47 @@ fn division_wraps_minimum_integer_by_negative_one() {
);
}
#[test]
fn remainder_handles_integer_boundaries() {
for (dividend, divisor, expected) in [
(i32::MAX - 1, i32::MAX, i32::MAX - 1),
(7, -3, -2),
(-7, 3, 2),
(i32::MIN, -1, 0),
(-1, i32::MIN, -1),
(1, i32::MIN, i32::MIN + 1),
(0, i32::MAX, 0),
] {
let mut m = mock();
run(&mut m, "scoreboard objectives add boundary dummy").unwrap();
run(
&mut m,
&format!("scoreboard players set left boundary {dividend}"),
)
.unwrap();
run(
&mut m,
&format!("scoreboard players set right boundary {divisor}"),
)
.unwrap();
run(
&mut m,
"scoreboard players operation left boundary %= right boundary",
)
.unwrap();
assert_eq!(
run(&mut m, "scoreboard players get left boundary").unwrap(),
expected,
"{dividend} %= {divisor}"
);
assert_eq!(
run(&mut m, "scoreboard players get right boundary").unwrap(),
divisor
);
}
assert!(apply_operation("%=", 1, 0).is_err());
}
#[test]
fn triggers() {
let mut m = mock();
+13
View File
@@ -1191,8 +1191,21 @@ def compute():
]
def scoreboard_remainder_boundaries():
operations = setup('/scoreboard objectives add boundary dummy')
for dividend, divisor in [(2147483646, 2147483647), (7, -3), (-2147483648, -1)]:
operations += setup(f'/scoreboard players set left boundary {dividend}',
f'/scoreboard players set right boundary {divisor}')
operations += [cmd('/scoreboard players operation left boundary %= right boundary'),
cmd('/scoreboard players get left boundary'),
cmd('/scoreboard players get right boundary')]
operations += setup('/scoreboard objectives remove boundary')
return operations
def social():
return [
*scoreboard_remainder_boundaries(),
*setup('/scoreboard objectives add trig trigger', '/scoreboard objectives add plain dummy'),
cmd('/trigger trig'),
cmd('/scoreboard players enable @s trig'),