104 lines
3.6 KiB
Plaintext
104 lines
3.6 KiB
Plaintext
Some questions and answers about doing math in UXN







Q: How can I handle negative integers in UXN?




A: Uxn doesn't have any builtin 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 (0127) 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 nonnegative


values, which is incorrect.


(example: `#ff #00 GTH` returns #01, but 1 is less than 0)




These implementations will safely compare "signed" bytes:




@signedlth ( x^ y^  x<y^ )


DUP2 #8080 AND2 EQU ?&diff LTH JMP2r &diff LTH #00 NEQ JMP2r




@signedgth ( 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 nonnegative, do unsigned


division (i.e. DIV) and then set the correct sign at the end.




@abssign ( x^  absx^ sign^ )


DUP #7f GTH #fe MUL INC STHk MUL STHr JMP2r




@signeddiv ( x^ y^  x/y^ )


abssign STH SWP abssign STH SWP DIV MULr STHr MUL 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'scomplement 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 signaware 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!
