! Fixed Point Number mathematical routines. ! Version 1.0 (19-Sep-2001) ! ! by Matt Albrecht - groboclown@users.sourceforge.net ! ! (If you need to edit this file, note that indentations are 4 spaces ! and tabs are not used.) ! ! This has been donated to the Public Domain. Use, abuse, and don't blame me. ! ! To prevent dirtying the global namespace, all members of this file begin with ! "fixedpt_", while members private to this file begin with "fixedpt__". ! Fixed-point math allows for decimal arithmetic using a fixed number of decimal ! places. This library uses the standard 16-bit word value to define 8 bits ! of non-decimal and 8 bits of decimal. ! Notes about arithmetic with fixed-points: ! The following operators act exactly the same in normal and fixed modes: ! + - < > <= >= == ! The sign operator (negation -) works the same, too. ! ++ and -- now perform an increment of 1/256 on the values. ! Modulo is meaningless. ! ! The only funky operations you need this for are multiplication and ! division. ! Due to signed / unsigned stuff, requires Z-Machine version 5 or older. System_file; ! C-like header! Ifndef FIXEDPT__INCLUDED; Constant FIXEDPT__INCLUDED; Message "Adding Fixed-point math library"; ! Depends upon the longint.h library Ifndef LONGINT__INCLUDED; Constant LONGINT__INCLUDED; Include "longint"; Endif; Include "math"; !------------------------------------------------------------------------------- ! Locale specific data Ifndef FIXEDPT_NEGATIVE_SIGN; Constant FIXEDPT_NEGATIVE_SIGN = "-"; Endif; Ifndef FIXEDPT_DECIMAL_POINT; Constant FIXEDPT_DECIMAL_POINT = "."; Endif; Ifndef FIXEDPT_DISPLAYED_DECIMALS; ! Number of decimal places to display. This cannot be < 1. Constant FIXEDPT_DISPLAYED_DECIMALS = 4; Endif; !------------------------------------------------------------------------------- ! Used for interior operations Array fixedpt__long1 -> 4; Array fixedpt__long2 -> 4; Array fixedpt__long3 -> 4; !------------------------------------------------------------------------------- ! Conversion routines !------------------------------------------------------------------------------- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Displays a fixed-point number as a signed decimal value ! Use by: ! print (fixedpt_signed)val; [ fixedpt_signed x ! parameter to display ; ! locals if (x < 0) { print (string)FIXEDPT_NEGATIVE_SIGN; ! remove the sign from the value x = -x; } fixedpt_unsigned( x ); ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Displays a fixed-point number as an unsigned decimal value ! Use by: ! print (fixedpt_signed)val; [ fixedpt_unsigned x ! parameter to display modulo count; ! locals ! display the upper part of the number. modulo = fixedpt__getUnsignedIntegerPart( x ); print (math_unsigned)modulo; ! display the decimal value. print (string)FIXEDPT_DECIMAL_POINT; x = fixedpt__getDecimalPart( x ); ! quick & simple check for easy case. if (x == 0) { print "0"; return; } ! long division for (count = 0 : count < FIXEDPT_DISPLAYED_DECIMALS : ++count) { ! next decimal place x = x * 10; ! find the character to display modulo = x / $100; !print "[x = ",x,", m = ",modulo,"]"; if (modulo > 10 || modulo < 0) { modulo = 0; } print (char)('0' + modulo); x = x - ($100 * modulo); if (x == 0) { ! don't display any more digits break; } } ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Convert the fixed-point number to an integer, ignoring the decimal points. ! This performs a fixed -> word conversion, as opposed to a fixed -> fixed ! conversion. [ fixedpt_unsigned_floor_word x; ! parameter return fixedpt__getUnsignedIntegerPart( x ); ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Convert the fixed-point number to an integer, ignoring the decimal points. ! This performs a fixed -> word conversion, as opposed to a fixed -> fixed ! conversion. [ fixedpt_signed_floor_word x; ! parameter return fixedpt__getSignedIntegerPart( x ); ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Convert the fixed-point number to an integer, rounding up to the next integer ! if the decimal part is > 0. ! This performs a fixed -> word conversion, as opposed to a fixed -> fixed ! conversion. [ fixedpt_unsigned_ceiling_word x ! parameter i; ! local i = fixedpt__getUnsignedIntegerPart( x ); if (fixedpt__getDecimalPart( x ) > 0) ++i; return i; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Convert the fixed-point number to an integer, rounding up to the next integer ! if the decimal part is > 0. ! This performs a fixed -> word conversion, as opposed to a fixed -> fixed ! conversion. [ fixedpt_signed_ceiling_word x ! parameter i; ! local i = fixedpt__getSignedIntegerPart( x ); if (fixedpt__getDecimalPart( x ) > 0) ++i; return i; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Convert the fixed-point number to an integer, rounding to the nearest integer. ! This performs a fixed -> word conversion, as opposed to a fixed -> fixed ! conversion. [ fixedpt_unsigned_round_word x ! parameter i; ! local i = fixedpt__getUnsignedIntegerPart( x ); if (fixedpt__getDecimalPart( x ) >= $80) ++i; return i; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Convert the fixed-point number to an integer, rounding to the nearest integer. ! This performs a fixed -> word conversion, as opposed to a fixed -> fixed ! conversion. [ fixedpt_signed_round_word x ! parameter i; ! local i = fixedpt__getSignedIntegerPart( x ); if (fixedpt__getDecimalPart( x ) >= $80) ++i; return i; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Convert a word integer into an unsigned fixed number. ! If the word value is >= 256, it is truncated to 255. [ fixedpt_word_to_unsigned_fixed x; ! parameter if (x > $ff) x = $ff; return math_unsigned_shift( x, 8 ); ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Convert a word integer into a signed fixed number. ! If the word value is >= 128, it is truncated to 127, and if it is ! < -128, it is truncated to -128. [ fixedpt_word_to_signed_fixed x; ! parameter if (x >= $80) { ! return biggest signed fixed return $7fff; } else if (x <= -128) { ! return smallest signed fixed return $8000; } if (x < 0) { return math_signed_shift( x, 8 ) - $ff; } return math_signed_shift( x, 8 ); ]; !------------------------------------------------------------------------------- ! Arithmetic !------------------------------------------------------------------------------- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Multiply two signed values together ! - due to possible overflow, this uses the longint routines to perform the ! operation. [ fixedpt_signed_mul x y ! parameters r s; ! locals ! operation = (x * y) >> 8; ! long1 = x math__set_signed_word_to_long( x, fixedpt__long1 ); ! long2 = y math__set_signed_word_to_long( y, fixedpt__long2 ); ! long3 = long1 * long2 LongMul( fixedpt__long3, fixedpt__long1, fixedpt__long2 ); ! Due to decimal place movement, the resulting value is in the top 3 bytes ! of long3. if (fixedpt__long3->0 < 0 && fixedpt__long3 ~= $ff) { ! overflow - return smallest signed fixed return $8000; } else if (fixedpt__long3->0 > 0) { ! overflow - return biggest signed fixed return $7fff; } ! no overflow r = fixedpt__long3->1 & $ff; s = fixedpt__long3->2 & $ff; return math_signed_shift( r, 8 ) + s; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Multiply two unsigned values together ! - due to possible overflow, this uses the longint routines to perform the ! operation. [ fixedpt_unsigned_mul x y ! parameters r s; ! locals ! operation = (x * y) >> 8; ! long1 = x math__set_unsigned_word_to_long( x, fixedpt__long1 ); ! long2 = y math__set_unsigned_word_to_long( y, fixedpt__long2 ); ! long3 = long1 * long2 LongMul( fixedpt__long3, fixedpt__long1, fixedpt__long2 ); if (fixedpt__long3->0 ~= 0) { ! overflow - return biggest unsigned fixed return $ffff; } ! no overflow r = fixedpt__long3->1 & $ff; s = fixedpt__long3->2 & $ff; return math_unsigned_shift( r, 8 ) + s; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Divide two signed values together (returns x / y) ! - due to possible overflow, this uses the longint routines to perform the ! operation. [ fixedpt_signed_div x y ! parameters r s; ! locals ! operation = (x / y) << 8; ! we don't want to loose precision, so perform the shifting first. ! long1 = x << 8 LongSet( fixedpt__long1, 0, math_unsigned_shift( x, 8 ) & $ff, (x & $ff), 0 ); if (x < 0) { fixedpt__long1->0 = $ff; } ! long2 = y math__set_signed_word_to_long( y, fixedpt__long2 ); ! long3 = long1 / long2 LongSignDiv( fixedpt__long3, fixedpt__long1, fixedpt__long2 ); r = fixedpt__long3->2 & $ff; s = fixedpt__long3->3 & $ff; return math_signed_shift( r, 8 ) + s; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Divide two unsigned values together (returns x / y) ! - due to possible overflow, this uses the longint routines to perform the ! operation. [ fixedpt_unsigned_div x y ! parameters r s; ! locals ! operation = (x / y) << 8; ! we don't want to loose precision, so perform the shifting first. ! long1 = x << 8 LongSet( fixedpt__long1, 0, math_unsigned_shift( x, -8 ) & $ff, (x & $ff), 0 ); ! long2 = y math__set_unsigned_word_to_long( y, fixedpt__long2 ); ! long3 = long1 / long2, long1 = modulo LongUnsignDivMod( fixedpt__long3, fixedpt__long1, fixedpt__long1, fixedpt__long2 ); r = fixedpt__long3->2 & $ff; s = fixedpt__long3->3 & $ff; return math_unsigned_shift( r, 8 ) + s; ]; !------------------------------------------------------------------------------- ! Private members !------------------------------------------------------------------------------- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Returns the upper 8 bits of the given word. [ fixedpt__getUnsignedIntegerPart x; ! parameters return math_unsigned_shift( x, -8 ) & $ff; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Returns the upper 8 bits of the given word. [ fixedpt__getSignedIntegerPart x; ! parameters ! no masking to keep the sign bits return math_signed_shift( x, -8 ); ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Returns the lower 8 bits of the given word. [ fixedpt__getDecimalPart x; ! parameters return x & $ff; ];