Floating point utilites






4.81/5 (17 votes)
A set of floating point utilities
Introduction
This is a set of floating point utilities. 16 functions are provided:
FloatsEqual
Testing float's for equality. When the operands of operators == and != are some form of floating type (float, double, or long double). Testing for equality between two floating point quantities is suspect because of round-off error and the lack of perfect representation of fractions.Round
Rounds a number to a specified number of digits.RoundDouble
Similar toRound()
above, but uses double's instead of float's.SigFig
Rounds a number to a specified number of significant figures.FloatToText
Converts a floating point number to ascii (without the appended zeros)CalcBase
This function wraps the given number so that it remains within its base. Returns a number between 0 and base - 1. For example if the base given was 10 and the parameter was 10 it would wrap it so that the number is now a 0. If the number given were -1, then the result would be 9. This function can also be used everywhere where a number needs to be kept within a certain range, for example angles (0 and 360) and radians (0 to TWO_PI).CalcBaseFloat
Same asCalcBase()
above, except using floatsAngle
Make sure angle is between 0 and 359LineLength
Calculates the length of a line between the following two pointsRoundValue
Converts a floating point value to an integer, very fastFloatToInt
Converts a floating point value to an integer, very fastFP_INV
This is about 2.12 times faster than using 1.0f / nCheckRange
Makes sure Value is within rangeCheckMin
Makes sure Value is >= MinCheckMax
Makes sure Value is <= MaxDivide
Performs a safe division
The credit for Round()
and RoundDouble()
goes to
Josef Wolfsteiner.
Modifications
- Simon Hughes, 18th November 2003.
- Updated
SigFig()
to check for 0.0 being passed in as the value, as log10f(0) returns NaN - Added
FloatsEqual()
function - Added
CalcBase()
function - Added
CalcBaseFloat()
function - Added
Angle()
function - Added
LineLength()
function - Modified
RoundValue()
function so it is much faster - Added
FloatToInt()
- Added
FP_INV
for very fast 1/n calculations - Added
CheckRange(), CheckMin(), CheckMax(), Divide()
template functions
- Updated
Header
// Testing float's for equality. When the operands of operators == and != are // some form of floating type (float, double, or long double). Testing for // equality between two floating point quantities is suspect because of // round-off error and the lack of perfect representation of fractions. // The value here is for testing two float values are equivalent within the // range shown here. The implementation is: // if(fabs(a - b) > float_equality) ... // See FloatsEqual(a, b) function #define float_equality 1.0e-20f bool FloatsEqual(const float &a, const float &b); // Rounds a number to a specified number of digits. // Number is the number you want to round. // Num_digits specifies the number of digits to which you want to round number. // If num_digits is greater than 0, then number is rounded to the // specified number of decimal places. // If num_digits is 0, then number is rounded to the nearest integer. // Examples // ROUND(2.15, 1) equals 2.2 // ROUND(2.149, 1) equals 2.1 // ROUND(-1.475, 2) equals -1.48 float Round(const float &number, const int num_digits); double RoundDouble(double doValue, int nPrecision); // Rounds X to SigFigs significant figures. // Examples // SigFig(1.23456, 2) equals 1.2 // SigFig(1.23456e-10, 2) equals 1.2e-10 // SigFig(1.23456, 5) equals 1.2346 // SigFig(1.23456e-10, 5) equals 1.2346e-10 // SigFig(0.000123456, 2) equals 0.00012 float SigFig(float X, int SigFigs); // Converts a floating point number to ascii (without the appended 0's) // Rounds the value if nNumberOfDecimalPlaces >= 0 CString FloatToText(float n, int nNumberOfDecimalPlaces = -1); // This function wraps the given number so that it remains within its // base. Returns a number between 0 and base - 1. // For example if the base given was 10 and the parameter was 10 it // would wrap it so that the number is now a 0. If the number given // were -1, then the result would be 9. This function can also be // used everywhere where a number needs to be kept within a certain // range, for example angles (0 and 360) and radians (0 to TWO_PI). int CalcBase(const int base, int num); // Same as CalcBase() above, except using floats float CalcBaseFloat(const float base, float num); // Make sure angle is between 0 and 359 int Angle(const int &angle); // Calculates the length of a line between the following two points float LineLength(const CPoint &point1, const CPoint &point2); //lint -save -e* // Converts a floating point value to an integer, very fast. inline int RoundValue(float param) { // Uses the FloatToInt functionality int a; int *int_pointer = &a; __asm fld param __asm mov edx,int_pointer __asm FRNDINT __asm fistp dword ptr [edx]; return a; } //lint -restore // At the assembly level the recommended workaround for the second // FIST bug is the same for the first; // inserting the FRNDINT instruction immediately preceding the // FIST instruction. // lint -e{715} // Converts a floating point value to an integer, very fast. inline void FloatToInt(int *int_pointer, const float &f) { __asm fld f __asm mov edx,int_pointer __asm FRNDINT __asm fistp dword ptr [edx]; } // This is about 2.12 times faster than using 1.0f / n // r = 1/p #define FP_INV(r,p) \ { \ int _i = 2 * 0x3F800000 - *(int *)&(p); \ (r) = *(float *)&_i; \ (r) = (r) * (2.0f - (p) * (r)); \ } // Makes sure Var is within range template<CLASS T> void CheckRange(T &Var, const T &Min, const T &Max) { if(Var < Min) Var = Min; else if(Var > Max) Var = Max; } // Makes sure Var is >= Min template<CLASS T> void CheckMin(T &Var, const T &Min) { if(Var < Min) Var = Min; } // Makes sure Var is <= Max template<CLASS T> void CheckMax(T &Var, const T &Max) { if(Var > Max) Var = Max; } // Performs a safe division. Checks that b is not zero before division. template<CLASS T> inline T Divide(const T &a, const T &b)
Source code
// Rounds a number to a specified number of digits. // Number is the number you want to round. // Num_digits specifies the number of digits to which you want // to round number. // If num_digits is greater than 0, then number is rounded // to the specified number of decimal places. // If num_digits is 0, then number is rounded to the nearest integer. // Examples // ROUND(2.15, 1) equals 2.2 // ROUND(2.149, 1) equals 2.1 // ROUND(-1.475, 2) equals -1.48 float Round(const float &number, const int num_digits) { float doComplete5i, doComplete5(number * powf(10.0f, (float) (num_digits + 1))); if(number < 0.0f) doComplete5 -= 5.0f; else doComplete5 += 5.0f; doComplete5 /= 10.0f; modff(doComplete5, &doComplete5i); return doComplete5i / powf(10.0f, (float) num_digits); } double RoundDouble(double doValue, int nPrecision) { static const double doBase = 10.0; double doComplete5, doComplete5i; doComplete5 = doValue * pow(doBase, (double) (nPrecision + 1)); if(doValue < 0.0) doComplete5 -= 5.0; else doComplete5 += 5.0; doComplete5 /= doBase; modf(doComplete5, &doComplete5i); return doComplete5i / pow(doBase, (double) nPrecision); } // Rounds X to SigFigs significant figures. // Examples // SigFig(1.23456, 2) equals 1.2 // SigFig(1.23456e-10, 2) equals 1.2e-10 // SigFig(1.23456, 5) equals 1.2346 // SigFig(1.23456e-10, 5) equals 1.2346e-10 // SigFig(0.000123456, 2) equals 0.00012 float SigFig(float X, int SigFigs) { if(SigFigs < 1) { ASSERT(FALSE); return X; } // log10f(0) returns NaN if(X == 0.0f) return X; int Sign; if(X < 0.0f) Sign = -1; else Sign = 1; X = fabsf(X); float Powers = powf(10.0f, floorf(log10f(X)) + 1.0f); return Sign * Round(X / Powers, SigFigs) * Powers; } // Converts a floating point number to ascii (without the appended 0's) // Rounds the value if nNumberOfDecimalPlaces >= 0 CString FloatToText(float n, int nNumberOfDecimalPlaces) { CString str; if(nNumberOfDecimalPlaces >= 0) { int decimal, sign; char *buffer = _fcvt((double)n, nNumberOfDecimalPlaces, &decimal, &sign); CString temp(buffer); // Sign for +ve or -ve if(sign != 0) str = "-"; // Copy digits up to decimal point if(decimal <= 0) { str += "0."; for(; decimal < 0; decimal++) str += "0"; str += temp; } else { str += temp.Left(decimal); str += "."; str += temp.Right(temp.GetLength() - decimal); } } else { str.Format("%-g", n); } // Remove appended zero's. "123.45000" become "123.45" int nFind = str.Find("."); if(nFind >= 0) { int nFinde = str.Find("e"); // 1.0e-010 Don't strip the ending zero if(nFinde < 0) { while(str.GetLength() > 1 && str.Right(1) == "0") str = str.Left(str.GetLength() - 1); } } // Remove decimal point if nothing after it. "1234." becomes "1234" if(str.Right(1) == ".") str = str.Left(str.GetLength() - 1); return str; } // Testing float's for equality. When the operands of operators == and != are // some form of floating type (float, double, or long double). Testing for // equality between two floating point quantities is suspect because of // round-off error and the lack of perfect representation of fractions. // The value here is for testing two float values are equivalent within the // range as specified by float_equality. bool FloatsEqual(const float &a, const float &b) { return (fabs(a - b) <= float_equality); } // This function wraps the given number so that it remains within its // base. Returns a number between 0 and base - 1. // For example if the base given was 10 and the parameter was 10 it // would wrap it so that the number is now a 0. If the number given // were -1, then the result would be 9. This function can also be // used everywhere where a number needs to be kept within a certain // range, for example angles (0 and 360) and radians (0 to TWO_PI). int CalcBase(const int base, int num) { if(num >= 0 && num < base) return num; // No adjustment neccessary if(num < 0) { num %= base; num += base; } else { num %= base; } return num; } // Same as CalcBase() above, except using floats float CalcBaseFloat(const float base, float num) { if(num >= 0.0f && num < base) return num; // No adjustment neccessary if(num < 0.0f) return fmodf(num, base) + base; return fmodf(num, base); } // Make sure angle is between 0 and 359 int Angle(const int &angle) { return CalcBase(360, angle); } // Calculates the length of a line between the following two points float LineLength(const CPoint &point1, const CPoint &point2) { const CPoint dist(point1 - point2); return sqrtf(float((dist.x * dist.x) + (dist.y * dist.y))); }