Click here to Skip to main content
15,867,453 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 305.8K   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

 
Questionwhere are the two functions called: RoundValue() and FloatToInt() in assembly ? Pin
Southmountain15-Oct-16 11:36
Southmountain15-Oct-16 11:36 
QuestionOk, but special numbers? Pin
MNorzagaray25-Apr-13 8:00
MNorzagaray25-Apr-13 8:00 
Generalconversion from float to double Pin
Roman Tarasov1-Dec-09 2:48
Roman Tarasov1-Dec-09 2:48 
GeneralRe: conversion from float to double Pin
Simon Hughes1-Dec-09 4:53
Simon Hughes1-Dec-09 4:53 
GeneralIMHO equality test, done this way, is rather arbitrary. Pin
CPallini31-Dec-07 10:49
mveCPallini31-Dec-07 10:49 
QuestionFaster Round()? Pin
Yap Chun Wei22-Jun-06 20:27
Yap Chun Wei22-Jun-06 20:27 
Will this simple Round function be faster?

double Round(double val, int dp)
{
int modifier = 1;
for (int i=0; i<dp; ++i) modifier *= 10;
return (floor(val*modifier+0.5)/modifier);
}
AnswerRe: Faster Round()? [modified] Pin
Simon Hughes24-Jun-06 13:11
Simon Hughes24-Jun-06 13:11 
GeneralRe: Faster Round()? [modified] Pin
Yap Chun Wei25-Jun-06 14:52
Yap Chun Wei25-Jun-06 14:52 
AnswerRe: Faster Round()? Pin
Simon Hughes26-Jun-06 8:42
Simon Hughes26-Jun-06 8:42 
GeneralRe: Faster Round()? Pin
bkrahmer26-Feb-10 12:36
bkrahmer26-Feb-10 12:36 
GeneralRe: Faster Round()? Pin
kanbang4-Aug-10 16:05
kanbang4-Aug-10 16:05 
GeneralRe: Faster Round()? Pin
Hoornet9322-Oct-07 21:04
Hoornet9322-Oct-07 21:04 
Generalneed help Pin
ravirevolt10-Mar-06 18:15
ravirevolt10-Mar-06 18:15 
GeneralDouble to Float Question Pin
T. Kulathu Sarma28-Nov-03 11:38
T. Kulathu Sarma28-Nov-03 11:38 
GeneralRe: Double to Float Question Pin
Nayan Choudhary1-Sep-04 22:25
professionalNayan Choudhary1-Sep-04 22:25 
GeneralVisual C++ float to string conversion problem Pin
8-Dec-00 5:47
suss8-Dec-00 5:47 
GeneralRe: Visual C++ float to string conversion problem Pin
Codin' Carlos20-Jan-02 13:20
Codin' Carlos20-Jan-02 13:20 
GeneralRe: Visual C++ float to string conversion problem Pin
tomasusan7-Nov-06 8:41
tomasusan7-Nov-06 8:41 
GeneralFast divides Pin
sigfpe22-Nov-00 10:01
sigfpe22-Nov-00 10:01 
GeneralMaintaining Significant Figures Pin
Dave Aebi28-Sep-00 16:12
Dave Aebi28-Sep-00 16:12 
GeneralSERIOUS Performance improvements for sqrt Pin
Simon Hughes27-Sep-00 5:18
Simon Hughes27-Sep-00 5:18 
GeneralRe: SERIOUS Performance improvements for sqrt Pin
Steven J. Ackerman29-Sep-00 11:00
Steven J. Ackerman29-Sep-00 11:00 
GeneralRe: SERIOUS Performance improvements for sqrt Pin
Simon Hughes2-Oct-00 3:12
Simon Hughes2-Oct-00 3:12 
GeneralRe: SERIOUS Performance improvements for sqrt Pin
Steven J. Ackerman2-Oct-00 7:18
Steven J. Ackerman2-Oct-00 7:18 
GeneralRe: SERIOUS Performance improvements for sqrt Pin
DQNOK18-Apr-07 12:20
professionalDQNOK18-Apr-07 12:20 

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.