Implement floored / and divisor-sign % operators (#745)

Fixes #703
This commit is contained in:
Rangi
2021-02-21 13:14:44 -08:00
committed by GitHub
parent a6d844a9a5
commit 5fd636ac4b
8 changed files with 85 additions and 9 deletions

View File

@@ -143,9 +143,16 @@ A great number of operators you can use in expressions are available (listed fro
complements a value by inverting all its bits.
.Pp
.Ic %
is used to get the remainder of the corresponding division.
.Sq 5 % 2
is 1.
is used to get the remainder of the corresponding division, so that
.Sq a / b * b + a % b == a
is always true.
The result has the same sign as the divisor.
This makes
.Sq a % b .
equal to
.Sq (a + b) % b
or
.Sq (a - b) % b .
.Pp
Shifting works by shifting all bits in the left operand either left
.Pq Sq <<
@@ -168,7 +175,8 @@ still evaluates both operands of
and
.Sq || .
.Pp
! returns 1 if the operand was 0, and 0 otherwise.
.Ic \&!
returns 1 if the operand was 0, and 0 otherwise.
.Ss Fixedpoint Expressions
Fixed-point numbers are basically normal (32-bit) integers, which count 65536th's instead of entire units, offering better precision than integers but limiting the range of values.
The upper 16 bits are used for the integer part and the lower 16 bits are used for the fraction (65536ths).

View File

@@ -283,6 +283,22 @@ static int32_t shift(int32_t shiftee, int32_t amount)
}
}
static int32_t 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));
}
static int32_t 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));
}
static int32_t exponent(int32_t base, uint32_t power)
{
int32_t result = 1;
@@ -410,17 +426,17 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
PRId32 "\n", INT32_MIN, INT32_MIN);
expr->nVal = INT32_MIN;
} else {
expr->nVal = src1->nVal / src2->nVal;
expr->nVal = divide(src1->nVal, src2->nVal);
}
break;
case RPN_MOD:
if (src2->nVal == 0)
fatalerror("Division by zero\n");
fatalerror("Modulo by zero\n");
if (src1->nVal == INT32_MIN && src2->nVal == -1)
expr->nVal = 0;
else
expr->nVal = src1->nVal % src2->nVal;
expr->nVal = modulo(src1->nVal, src2->nVal);
break;
case RPN_EXP:
if (src2->nVal < 0)

View File

@@ -21,6 +21,22 @@
#include "extern/err.h"
static int32_t 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));
}
static int32_t 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));
}
static int32_t exponent(int32_t base, uint32_t power)
{
int32_t result = 1;
@@ -235,7 +251,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
popRPN();
value = INT32_MAX;
} else {
value = popRPN() / value;
value = divide(popRPN(), value);
}
break;
case RPN_MOD:
@@ -247,7 +263,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
popRPN();
value = 0;
} else {
value = popRPN() % value;
value = modulo(popRPN(), value);
}
break;
case RPN_UNSUB:

36
test/asm/div-mod.asm Normal file
View File

@@ -0,0 +1,36 @@
_ASM equ 0
test: MACRO
; Test RGBASM
V equs "_ASM +"
static_assert \#
PURGE V
; Test RGBLINK
V equs "_LINK +"
assert \#
PURGE V
ENDM
for x, -300, 301
for y, -x - 1, x + 2
if y != 0
q = x / y
r = x % y
test (V (q * y + r)) == (V x)
test (V (x + y) % y) == (V r)
test (V (x - y) % y) == (V r)
endc
endr
endr
for x, -300, 301
for p, 31
y = 2 ** p
r = x % y
m = x & (y - 1)
test (V r) == (V m)
endr
endr
SECTION "LINK", ROM0
_LINK::

0
test/asm/div-mod.err Normal file
View File

0
test/asm/div-mod.out Normal file
View File

0
test/asm/div-mod.out.bin Normal file
View File

0
test/asm/math.out.bin Normal file
View File