Fix division and modulo for very large negative numbers (#1790)

This commit is contained in:
Rangi
2025-08-11 20:46:47 -04:00
committed by GitHub
parent 30a8503dcd
commit 7df9c12a6c
5 changed files with 91 additions and 10 deletions

View File

@@ -11,15 +11,16 @@
int32_t op_divide(int32_t dividend, int32_t divisor) {
// Adjust division to floor toward negative infinity,
// not truncate toward zero
return dividend / divisor - ((dividend % divisor < 0) != (divisor < 0));
int32_t remainder = dividend % divisor;
return dividend / divisor - (remainder != 0 && (remainder < 0) != (divisor < 0));
}
int32_t op_modulo(int32_t dividend, int32_t divisor) {
int32_t remainder = dividend % divisor;
// Adjust modulo to have the sign of the divisor,
// not the sign of the dividend
return remainder + divisor * ((remainder < 0) != (divisor < 0));
return static_cast<int32_t>(
dividend - static_cast<int64_t>(op_divide(dividend, divisor)) * divisor
);
}
int32_t op_exponent(int32_t base, uint32_t power) {

47
test/asm/div-negative.asm Normal file
View File

@@ -0,0 +1,47 @@
MACRO test
def num = \1
def den = \2
def quo = num / den
def rem = num % den
def rev = quo * den + rem
assert num == rev
println "{ 11d:num} (${08x:num}) / { 11d:den} (${08x:den}) = { 11d:quo} (${08x:quo}) R { 11d:rem} (${08x:rem})"
ENDM
test $8000_0000, $8000_0000
test $8000_0000, $c000_0000
test $8000_0000, $f000_0000
test $8000_0000, $f800_0000
test $8000_0000, $ff00_0000
test $c000_0000, $8000_0000
test $c000_0000, $c000_0000
test $c000_0000, $f000_0000
test $c000_0000, $f800_0000
test $c000_0000, $ff00_0000
test $f800_0000, $8000_0000
test $f800_0000, $c000_0000
test $f800_0000, $f000_0000
test $f800_0000, $f800_0000
test $f800_0000, $ff00_0000
test $0000_0000, $8000_0000
test $0000_0000, $c000_0000
test $0000_0000, $f000_0000
test $0000_0000, $f800_0000
test $0000_0000, $ff00_0000
test $0080_0000, $ff80_0000
test $0300_0000, $fd00_0000
test $0300_0000, $ff00_0000
test $0300_0000, $ff80_0000
test $6000_0000, $fd00_0000
test $6000_0000, $ff00_0000
test $6000_0000, $ff80_0000
test $7f00_0000, $fd00_0000
test $7f00_0000, $ff00_0000
test $7f00_0000, $ff80_0000

30
test/asm/div-negative.out Normal file
View File

@@ -0,0 +1,30 @@
-2147483648 ($80000000) / -2147483648 ($80000000) = 1 ($00000001) R 0 ($00000000)
-2147483648 ($80000000) / -1073741824 ($c0000000) = 2 ($00000002) R 0 ($00000000)
-2147483648 ($80000000) / -268435456 ($f0000000) = 8 ($00000008) R 0 ($00000000)
-2147483648 ($80000000) / -134217728 ($f8000000) = 16 ($00000010) R 0 ($00000000)
-2147483648 ($80000000) / -16777216 ($ff000000) = 128 ($00000080) R 0 ($00000000)
-1073741824 ($c0000000) / -2147483648 ($80000000) = 0 ($00000000) R -1073741824 ($c0000000)
-1073741824 ($c0000000) / -1073741824 ($c0000000) = 1 ($00000001) R 0 ($00000000)
-1073741824 ($c0000000) / -268435456 ($f0000000) = 4 ($00000004) R 0 ($00000000)
-1073741824 ($c0000000) / -134217728 ($f8000000) = 8 ($00000008) R 0 ($00000000)
-1073741824 ($c0000000) / -16777216 ($ff000000) = 64 ($00000040) R 0 ($00000000)
-134217728 ($f8000000) / -2147483648 ($80000000) = 0 ($00000000) R -134217728 ($f8000000)
-134217728 ($f8000000) / -1073741824 ($c0000000) = 0 ($00000000) R -134217728 ($f8000000)
-134217728 ($f8000000) / -268435456 ($f0000000) = 0 ($00000000) R -134217728 ($f8000000)
-134217728 ($f8000000) / -134217728 ($f8000000) = 1 ($00000001) R 0 ($00000000)
-134217728 ($f8000000) / -16777216 ($ff000000) = 8 ($00000008) R 0 ($00000000)
0 ($00000000) / -2147483648 ($80000000) = 0 ($00000000) R 0 ($00000000)
0 ($00000000) / -1073741824 ($c0000000) = 0 ($00000000) R 0 ($00000000)
0 ($00000000) / -268435456 ($f0000000) = 0 ($00000000) R 0 ($00000000)
0 ($00000000) / -134217728 ($f8000000) = 0 ($00000000) R 0 ($00000000)
0 ($00000000) / -16777216 ($ff000000) = 0 ($00000000) R 0 ($00000000)
8388608 ($00800000) / -8388608 ($ff800000) = -1 ($ffffffff) R 0 ($00000000)
50331648 ($03000000) / -50331648 ($fd000000) = -1 ($ffffffff) R 0 ($00000000)
50331648 ($03000000) / -16777216 ($ff000000) = -3 ($fffffffd) R 0 ($00000000)
50331648 ($03000000) / -8388608 ($ff800000) = -6 ($fffffffa) R 0 ($00000000)
1610612736 ($60000000) / -50331648 ($fd000000) = -32 ($ffffffe0) R 0 ($00000000)
1610612736 ($60000000) / -16777216 ($ff000000) = -96 ($ffffffa0) R 0 ($00000000)
1610612736 ($60000000) / -8388608 ($ff800000) = -192 ($ffffff40) R 0 ($00000000)
2130706432 ($7f000000) / -50331648 ($fd000000) = -43 ($ffffffd5) R -33554432 ($fe000000)
2130706432 ($7f000000) / -16777216 ($ff000000) = -127 ($ffffff81) R 0 ($00000000)
2130706432 ($7f000000) / -8388608 ($ff800000) = -254 ($ffffff02) R 0 ($00000000)

View File

@@ -1,6 +1,7 @@
def fzero equs "startof(\"test\")"
section "test", rom0
ld a, $8000_0000 / (fzero - 1)
ld a, $8000_0000 / (fzero - 2)
ld a, 1 << (fzero - 1)
ld a, 1 << (fzero + 32)
ld a, (fzero - 1) >> 1

View File

@@ -1,16 +1,18 @@
warning: Shifting right by large amount 32 [-Wshift-amount]
at patch-diagnostics.asm(11)
warning: Shifting right by negative amount -1 [-Wshift-amount]
at patch-diagnostics.asm(10)
warning: Shifting right by negative amount -1 [-Wshift-amount]
at patch-diagnostics.asm(9)
warning: Shifting right by large amount 32 [-Wshift-amount]
at patch-diagnostics.asm(8)
at patch-diagnostics.asm(9)
warning: Shifting right by negative amount -1 [-Wshift-amount]
at patch-diagnostics.asm(7)
at patch-diagnostics.asm(8)
warning: Shifting right negative value -1 [-Wshift]
at patch-diagnostics.asm(6)
at patch-diagnostics.asm(7)
warning: Shifting left by large amount 32 [-Wshift-amount]
at patch-diagnostics.asm(5)
at patch-diagnostics.asm(6)
warning: Shifting left by negative amount -1 [-Wshift-amount]
at patch-diagnostics.asm(5)
warning: Value $40000000 is not 8-bit [-Wtruncation]
at patch-diagnostics.asm(4)
warning: Division of -2147483648 by -1 yields -2147483648 [-Wdiv]
at patch-diagnostics.asm(3)