/*
This program is distributed under the terms of the 'MIT license'. The text
of this licence follows...
Copyright (c) 2004-2005 J.D.Medhurst (a.k.a. Tixy)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/**
@file
@brief 32-Bit fixed-point maths routines
*/
#ifndef __FIX_H__
#define __FIX_H__
/**
@defgroup fix Maths - 32bit Fixed-Point Maths
@brief Routines for performing 32bit fixed-point maths.
This code is targeted at CPUs without hardware support for division or floating point.
It has been optimised for ARM CPUs but should be suitable for other RISC architecures.
@version 2006-05-20
- Changed code to use standard typedefs, e.g. replaced uint8 with uint8_t, and made use of size_t.
@version 2006-06-04
- Added options to tune implementation of Fix::Mul(), Fix::MulNS() and Fix::Div().
See #FIX_USE_64BIT_MUL and #FIX_UNROLL_DIV_LOOP.
@{
*/
/**
A type representing a 32bit fixed-point number.
The binary point lies between bits 15 and 16.
To convert between floating point and fixed point representations, numbers need
scaling by 2 to-the-power-of 16 (65536) e.g.
@code
fix x;
float f;
// to convert f into x...
x = (double)f*65536.0l;
// to convert x into f...
f = (double)x/65536.0l;
@endcode
Note, values are cast using (double) here to persuade the compiler to use
double precision floating point arithmetic. This avoids loosing precision as single
floats only have 24 significant bits, (less that the 32 bits in a fix number).
To convert between int and fixed point types, shift values by 16.
@code
fix x;
int i;
// to convert i into x...
x = i<<16;
// to convert x into i (rounding towards minus infinity)...
i = x>>16;
// to convert x into i (rounding towards nearest integer)...
i = ((x>>15)+1)>>1;
@endcode
Conversions described above may produce results which overflow the
range which can be represented by fixed point numbers. To detect
this, check the range of values before they are converted into fixed point...
@code
fix x;
float f;
// convert f into x...
float temp = (double)f*65536.0l;
if((double)-2147483648.0<=temp && temp<=(double)2147483647.0)
x = temp;
else
printf("Number conversion overflow!");
@endcode
or...
@code
fix x;
int i;
// convert i into x...
if(-0x8000<=i && i<=0x7fff)
x = i<<16;
else
printf("Number conversion overflow!");
@endcode
*/
typedef int32_t fix;
/**
A type representing a 32bit unsigned fixed-point number.
The binary point lies between bits 15 and 16.
For convertsion into/from fixed point format, see #fix.
Note, valid input range fo unsigned values is 0..0xffff for integers, and
0..0xffffffff for the 'temp' value in the floating point conversion.
*/
typedef uint32_t ufix;
/**
A 32bit fixed-point value representing an angle.
Values are scaled by a factor of 1/(2*PI), i.e. a value of 1.0 (0x10000)
represents a full circle.
To convert angles between floating point and fixed point representations,
numbers need scaling as for #fix, but with the scaling factor reduced
by 2*PI. E.g.
@code
#define PI 3.1415926535897932384626433832795l
fixangle a;
double f;
// to convert f into a...
a = (double)f*65536.0l/(2.0l*PI);
// to convert a into f...
f = (double)a/65536.0l*(2.0l*PI);
@endcode
This treats f as an angle in radians, (the usual units for angles
in mathematics and computer functions). If you want to use values in degrees,
replace 2.0l*PI with 360.0l.
*/
typedef fix fixangle;
/**
@brief Fixed point arithmetic functions.
Operands are 32 bits in size with the binary point between bits 15 and 16.
@see fix
ufix
fixangle
@version 2005-02-26
- Added Random(uint32_t& seed)
- Added Random(uint32_t& seed,ufix range)
*/
class Fix
{
public:
/**
Add two fixed-point numbers.
Produces saturated result on overflow.
When the addition of two numbers is know not to cause
overflow, or when this doesn't matter, normal integer addition (+)
can be used instead...
@code
fix a;
fix b;
fix sum = a+b;
@endcode
@param a Augend
@param b Addend
@return a+b.
If a+b>0x7FFF.FFFF then 0x7FFF.FFFF is returned.
If a+b<-0x8000.0000 then -0x8000.0000 is returned.
*/
IMPORT static fix Add(fix a,fix b);
/**
Subtract two fixed-point numbers.
Produces saturated result on overflow.
When the subtraction of two numbers is know not to cause
overflow, or when this doesn't matter, normal integer subtraction (-)
can be used instead...
@code
fix a;
fix b;
fix difference = a-b;
@endcode
@param a Minuend
@param b Subtrahend
@return a-b.
If a-b>0x7FFF.FFFF then 0x7FFF.FFFF is returned.
If a-b<-0x8000.0000 then -0x8000.0000 is returned.
*/
IMPORT static fix Sub(fix a,fix b);
/**
Multiply two fixed-point numbers.
Produces saturated result on overflow.
To multiply a fixed point number by an integer, normal integer multiplication (*)
may be used...
@code
fix a;
fix twoTimes = a*2;
@endcode
This does not detect overflow.
@param a Multiplicand
@param b Multiplier
@return a*b.
If a*b>0x7FFF.FFFF then 0x7FFF.FFFF is returned.
If a*b<-0x8000.0000 then -0x8000.0000 is returned.
@see MulNS()
*/
IMPORT static fix Mul(fix a,fix b);
/**
Multiply two fixed-point numbers.
This is a Non-Saturating (and faster) version of Mul().
On overflow the result is undefined.
To multiply a fixed point number by an integer, normal integer multiplication (*)
may be used...
@code
fix a;
fix twoTimes = a*2;
@endcode
@param a Multiplicand
@param b Multiplier
@return a*b.
If a*b>0x7FFF.FFFF or a*b<-0x8000.0000 then the returned result is undefined.
*/
IMPORT static fix MulNS(fix a,fix b);
/**
Divide two fixed-point numbers.
Produces saturated result on overflow.
Division by zero is treated as division by a very small number and
produces a saturated result accordingly.
To divide a fixed point number by an integer, normal integer division (/)
may be used...
@code
fix a;
fix half = a/2;
@endcode
This does not error conditions.
@param a Dividend
@param b Divisor
@return a/b.
If a/b>0x7FFF.FFFF then 0x7FFF.FFFF is returned.
If a/b<-0x8000.0000 then -0x8000.0000 is returned.
If b==0 and a>=0 then 0x7FFF.FFFF is returned.
If b==0 and a<0 then -0x8000.0000 is returned.
*/
IMPORT static fix Div(fix a,fix b);
/**
Calculate the square root of a fixed-point number.
@param a Unsigned fixed point number.
@return a^0.5.
*/
IMPORT static fix Sqrt(ufix a);
/**
Calculate the logarithm to base 2 of a fixed-point number .
Accuracy is +/-8.40e-6 (+/-0.55 lsb).
@param a Unsigned fixed point number.
@return log2(a).
If a==0 then -0x8000.0000 is returned
*/
IMPORT static fix Log2(ufix a);
/**
Raise 2 to-the-power of a fixed-point number, (2^a).
Accuracy is +/-1.14e-5 (+/-0.75 lsb).
@param a Fixed point number.
@return 2^a.
If 2^a>0xFFFF.FFFF then 0xFFFF.FFFF is returned.
*/
IMPORT static ufix Exp2(fix a);
/**
Calculate the Sine of an angle.
Accuracy is +/-8.55e-6 (+/-0.56 lsb).
@param angle The angle.
@return The Sine of \a angle.
*/
IMPORT static fix Sin(fixangle angle);
/**
Calculate the Cosine of an angle.
Accuracy is +/-8.55e-6 (+/-0.56 lsb).
@param angle The angle.
@return The Cosine of \a angle.
*/
IMPORT static fix Cos(fixangle angle);
/**
Calculate the Tangent of an angle.
Accuracy is +/-1.01e-5 (+/-0.66 lsb).
@param angle The angle.
@return The Tangent of \a angle.
If Tan(\a angle)>0x7FFF.FFFF then 0x7FFF.FFFF is returned.
If Tan(\a angle)<-0x8000.0000 then -0x8000.0000 is returned.
*/
IMPORT static fix Tan(fixangle angle);
/**
Calculate the Arc-Sine of a value.
Accuracy is +/-8.55e-6 (+/-0.56 lsb).
@param value The value. Should be in the range -1.0 to 1.0 (-0x10000 to 0x10000).
@return The Arc-Sine of \a value.
If \a value<-1.0 then -0.25 (-0x4000) is returned.
If \a value>1.0 then 0.25 (0x4000) is returned.
*/
IMPORT static fixangle ASin(fix value);
/**
Calculate the Arc-Cosine of a value.
Accuracy is +/-8.55e-6 (+/-0.56 lsb).
@param value The value. Should be in the range -1.0 to 1.0 (-0x10000 to 0x10000).
@return The Arc-Cosine of \a value.
If \a value<-1.0 then 0.5 (0x8000) is returned.
If \a value>1.0 then 0.0 (0x0000) is returned.
*/
IMPORT static fixangle ACos(fix value);
/**
Calculate the Arc-Tangent of a value.
Accuracy is +/-9.00e-6 (+/-0.59 lsb).
@param value The value.
@return The Arc-Tangent of \a value.
*/
IMPORT static fixangle ATan(fix value);
/**
Generate a psuedo-random number.
@param seed A reference to the seed value. This will be updated after each call
to this function.
@return A pseudo-random number
@since 2005-02-26
*/
IMPORT static fix Random(uint32_t& seed);
/**
Generate a psuedo-random number.
@param seed A reference to the seed value. This will be updated after each call
to this function.
@param range The range for the generated numbers.
@return A pseudo-random number less than the value of \a range
@since 2005-02-26
*/
IMPORT static ufix Random(uint32_t& seed,ufix range);
};
/** @} */ // End of group
#endif