RCodePlus Double Precision Floating Point |
The floating point routines use one array as a FPU (floating point unit) register bank. The syntax is rather klunky if you write RCodePlus directly. Look at the debug code in the default RCodePlus personality (Mini-ObeyCat) for a simple test program.
Preparing to use floating point
Determine the number of floating point registers you need, and multiply it by 4 (8 bytes per FPU register). Use that number (##) in the following code:
LOCAL fpu_array 25
AP_DIM fpu_array ## // ## = number of FPU registers times 4
SET AP_fpu fpu_array // use this array from now on
All DBL_ operations use indices into this array (that are first multiplied by 8 bytes). So your indices you use start with 0 and go up by 1.
Getting values in and out of FPU registers
To get floating point values in and out of RCODE variables (16 bit signed integers), a simple fixed point representation is used. The int_part is the integer part, and fract_part is the fractional part multiplied by 10000. For positive numbers, you can think of it as a "%d.%04d" format. Negative numbers get tricky (the int_part carries the sign, the fract_part does not).The complete definition is:
// float_value = int_part . fract_part
if (int_part > 0)
{
// positive
assert(fract_part >= 0 && fract_part < 10000);
float_value = int_part + fract_part / 10000;
}
else if (int_part < 0)
{
// negative, fract_part is not signed
assert(fract_part >= 0 && fract_part < 10000);
float_value = int_part - fract_part / 10000;
}
else
{
// int_part = 0, in this case, fract_part is signed
assert(fract_part > -10000 && fract_part < 10000);
float_value = int_part + fract_part / 10000;
}
Try to keep values in the FPU registers for as long as you can, and only convert to RCODE variables when you need to.
Setting a FPU register (DBL_SET) comes in two forms, a constant or from another FPU register. Retreiving the current value (int_part, fract_part, perhaps with loss of precision) is done with DBL_GET. You can
DBL_SET index int_part fract_part // fpu[index] = int_part . fract_part
DBL_SET index index2 // fpu[index] = fpu
DBL_GET index variable_for_int_part variable_for_fract_part
For example:
DBL_SET 1 10 5 // fpu[0] = 10.5
DBL_GET 1 a b // a = 10, b = 5
Binary Operations
The format of the binary operations are all the same. The first index is the destination (an FPU register), followed by the index of the first operand (an FPU register), followed by either one or two numbers. If one number, the second operand is an FPU register. If two numbers, the second operand is an immediate constant value (int_part . fract_part)
DBL_ADD index0 index1 index2 // fpu[index0] = fpu[index1] + fpu[index2]
DBL_ADD index0 index1 int_part fract_part // fpu[index0] = fpu[index1] + int_part . fract_part
For example:
DBL_ADD 9 9 1 0 // fpu[9] += 1.0
All the binary operators have the same format:
- DBL_ADD : addition
- DBL_SUB : subtraction
- DBL_MUL : multiply
- DBL_DIV : divide
- DBL_ATAN2 : atan2() = arctan(arg1 / arg2) - in radians
Unary Operations
The format of the unary operations are all the same. The first index is the destination (an FPU register), followed by either one or two numbers. If one number, the operand is an FPU register. If two numbers, it is an immediate constant value (int_part . fract_part)
DBL_SIN index0 index1 // fpu[index0] = sin ( fpu[index1] )
DBL_SIN index0 int_part fract_part // fpu[index0] = sin ( int_part . fract_part )
For example:
DBL_SIN 7 2 // fpu[7] = sin(fpu[2])
DBL_EXP 8 1 0 // fpu[8] += e ^ 1 = e
All unary operators have the same format:
- DBL_SIN : sin() - in radians
- DBL_TAN : tan() - in radian
- DBL_SQRT : square root
- DBL_EXP : exp() - power of e
- DBL_LOG : ln() - log base e
- (oops ASIN/ACOS missing)