20231025 13:03:52 04:00



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.





20231229 16:35:39 05:00



@abssign ( x^  absx^ sign^ )

20231229 15:41:13 05:00



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





20231025 13:03:52 04:00



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

20231229 16:35:39 05:00



abssign STH SWP abssign STH SWP DIV MULr STHr MUL JMP2r

20231025 13:03:52 04:00







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!
