! Fixed Point Number mathematical routines.
! Version 1.0 (19-Sep-2001)
!
! by Matt Albrecht -
[email protected]
!
! (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;
];