math notes
This commit is contained in:
parent
64ccfc1aa2
commit
8cea231772
|
@ -0,0 +1,100 @@
|
|||
Some questions and answers about doing math in UXN
|
||||
--------------------------------------------------
|
||||
|
||||
Q: How can I handle negative integers in UXN?
|
||||
|
||||
A: Uxn doesn't have any built-in support for signed integers. However, you can
|
||||
emulate signed numbers using unsigned numbers by treating some of the values
|
||||
as having different (negative) values from their unsigned values.
|
||||
|
||||
For example, treating unsigned bytes as signed results in the following:
|
||||
|
||||
unsigned unsigned signed
|
||||
hex decimal decimal
|
||||
#00 0 0
|
||||
#01 1 1
|
||||
#02 2 2
|
||||
... ... ...
|
||||
#7e 126 126
|
||||
#7f 127 127
|
||||
#80 128 -128
|
||||
#81 129 -127
|
||||
#82 130 -126
|
||||
... ... ...
|
||||
#fd 253 -3
|
||||
#fe 254 -2
|
||||
#ff 255 -1
|
||||
|
||||
The first 128 integers (0-127) are represented the same as unsigned and signed,
|
||||
but the latter 128 are different. The basic idea here is that for values
|
||||
greater than #7f (127) we subtract 256 to get their "signed value":
|
||||
|
||||
signed(n) = if n > 127 then n else n - 256
|
||||
|
||||
It turns out that many unsigned operations "work" even when treating the values
|
||||
as signed. (In other words, you get the same result as you would have using a
|
||||
language with signed integer types.) The following arithmetic instructions work
|
||||
correctly with "signed" values:
|
||||
|
||||
EQU and NEQ
|
||||
ADD (example: `#13 #ff ADD` returns #12)
|
||||
SUB (exampel: `#02 #03 SUB` returns #ff)
|
||||
MUL (example: `#02 #ff MUL` returns #fe)
|
||||
|
||||
Other instructions will not handle "negative" integers correctly:
|
||||
|
||||
GTH and LTH:
|
||||
|
||||
1. these work correctly when comparing values with the same sign
|
||||
(example: `#80 #ff LTH` returns #01)
|
||||
|
||||
2. however, LTH will consider negative values greater than non-negative
|
||||
values, which is incorrect.
|
||||
(example: `#ff #00 GTH` returns #01, but -1 is less than 0)
|
||||
|
||||
These implementations will safely compare "signed" bytes:
|
||||
|
||||
@signed-lth ( x^ y^ -- x<y^ )
|
||||
DUP2 #8080 AND2 EQU ?&diff LTH JMP2r &diff LTH #00 NEQ JMP2r
|
||||
|
||||
@signed-gth ( x^ y^ -- x<y^ )
|
||||
DUP2 #8080 AND2 EQU ?&diff GTH JMP2r &diff GTH #00 NEQ JMP2r
|
||||
|
||||
DIV:
|
||||
|
||||
Similarly, division will not correctly handle signed values. The simplest
|
||||
way to handle this is to make both values non-negative, do unsigned
|
||||
division (i.e. DIV) and then set the correct sign at the end.
|
||||
|
||||
@signed-div ( x^ y^ -- x/y^ )
|
||||
DUP2 #8080 AND2 EQU STH DIV STHr ?&same #ff MUL &same JMP2r
|
||||
|
||||
Be careful! The smallest negative value (-128 for bytes, -32768 for shorts)
|
||||
has no corresponding positive value. This means that some operations will
|
||||
not work as expected:
|
||||
|
||||
`#80 #ff MUL` returns #80 (-128 * -1 = -128)
|
||||
`#00 #80 SUB` returns #80 (0 - (-128) = -128)
|
||||
|
||||
Also, negative and positive values will "wrap around" in the usual way when
|
||||
dealing with two's-complement representations:
|
||||
|
||||
`#7f #01 ADD` returns #80 (127 + 1 = -128)
|
||||
`#80 #01 SUB` returns #7f (-128 - 1 = 127)
|
||||
`#80 #80 ADD` returns #00 (-128 + (-128) = 0)
|
||||
|
||||
SFT:
|
||||
|
||||
The unsigned shift operator treats the sign bit like any other. This means
|
||||
shifting left will lose the sign bit (reversing the sign) and that shifting
|
||||
right will convert the sign bit into a value bit.
|
||||
|
||||
If you need a sign-aware shift you'll likely want to convert negatives to
|
||||
positive values, perform a shift, and then restore the sign. Keep in mind
|
||||
that -128 cannot be converted to a positive value, and may require special
|
||||
treatment.
|
||||
|
||||
Signed numbers will also need their own routines for decimal input and output,
|
||||
if those are required by your program.
|
||||
|
||||
Good luck!
|
Loading…
Reference in New Issue