Click here to Skip to main content
15,881,852 members
Articles / Programming Languages / C++
Article

Floating point utilites

Rate me:
Please Sign up or sign in to vote.
4.81/5 (17 votes)
17 Nov 2003CPOL 306.4K   1.9K   67   66
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 to Round() 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 as CalcBase() above, except using floats
  • Angle Make sure angle is between 0 and 359
  • LineLength Calculates the length of a line between the following two points
  • RoundValue Converts a floating point value to an integer, very fast
  • FloatToInt Converts a floating point value to an integer, very fast
  • FP_INV This is about 2.12 times faster than using 1.0f / n
  • CheckRange Makes sure Value is within range
  • CheckMin Makes sure Value is >= Min
  • CheckMax Makes sure Value is <= Max
  • Divide 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

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)));
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) www.ByBox.com
United Kingdom United Kingdom
C++ and C# Developer for 21 years. Microsoft Certified.

UK Senior software developer / team leader.

I've been writing software since 1985. I pride myself on designing and creating software that is first class. That means it has to be fast, scalable, and with good use of design patterns.

I have done everything from risk analysis and explosion modelling, banking systems, to highly scalable multi-threaded arrival and departure screens in many leading airports, to state of the art wireless warehouse systems.

Comments and Discussions

 
GeneralIMHO equality test, done this way, is rather arbitrary. Pin
CPallini31-Dec-07 10:49
mveCPallini31-Dec-07 10:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.