Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C

Some handy decimal functions for double

3.22/5 (10 votes)
20 Feb 2008CPOL1 min read 1  
Rounding, decimal places, etc..

Introduction

I use the functions in math.h a lot. But, some functions are missing, so I wrote my own. They are simple functions dealing with decimal places. Nothing big and fancy. But they can be handy sometimes.

Using the code

The functions are straightforward and self-explaining. These functions depend on math.h (and math.h only).

C++
#include "math.h"

Round() rounds a double to a certain number of decimal places. It is very similar to Excel's Round function.

C++
// rounds a double variable to nPlaces decimal places
double Round(double dbVal, int nPlaces /* = 0 */)
{
    const double dbShift = pow(10.0, nPlaces);
    return  floor(dbVal * dbShift + 0.5) / dbShift; 
}

GetDecimalPlaces() counts and returns the number of decimal places of a double (refer to note 1).

C++
// get the number of decimal places
int GetDecimalPlaces(double dbVal)
{
    static const int MAX_DP = 10;
    static const double THRES = pow(0.1, MAX_DP);
    if (dbVal == 0.0)
        return 0;
    int nDecimal = 0;
    while (dbVal - floor(dbVal) > THRES && nDecimal < MAX_DP)
    {
        dbVal *= 10.0;
        nDecimal++;
    }
    return nDecimal;
}

GetFmtStr() makes a printf()-style format string.

C++
// get the number format string
const char* GetFmtStr(char* szFmt, int nDecimal)
{
    sprintf(szFmt, "%%.%dlf", nDecimal);
    return szFmt;
}

Finally, RoundUD() rounds up or rounds down a double to its nearest value in dbUnit steps.

C++
// round up/down to certain units
double RoundUD(bool bRoundUp, double dbUnit, double dbVal)
{
    static const int ROUND_DP = 10;
    double dbValInUnit = dbVal / dbUnit;
    dbValInUnit = Round(dbValInUnit, ROUND_DP);
    if (bRoundUp) // round up
        dbValInUnit = ceil(dbValInUnit);
    else // round down
        dbValInUnit = floor(dbValInUnit);
    return (dbValInUnit * dbUnit);
}

You can test the functions above with the following code:

C++
printf("rounding %lf d.p.=%d is %lf\n", 10.0,        1,    Round(10.0,         1));
printf("rounding %lf d.p.=%d is %lf\n", 0.0,         0,    Round(0.0,          1));
printf("rounding %lf d.p.=%d is %lf\n", 0.1,         1,    Round(0.1,          1));
printf("rounding %lf d.p.=%d is %lf\n", 0.01,        1,    Round(0.01,         1));
printf("rounding %lf d.p.=%d is %lf\n", 0.0123456,   0,    Round(0.0123456,    0));
printf("rounding %lf d.p.=%d is %lf\n", 0.0123456,   1,    Round(0.0123456,    1));
printf("rounding %lf d.p.=%d is %lf\n", 0.0123456,   2,    Round(0.0123456,    2));
printf("rounding %lf d.p.=%d is %lf\n", 0.0123456,   6,    Round(0.0123456,    6));
printf("rounding %lf d.p.=%d is %lf\n", -0.0123456,  6,    Round(-0.0123456,   6));
printf("rounding %lf up unit=%lf is %lf\n",   0.0,   0.05, RoundUD(true, 0.05, 0.0));
printf("rounding %lf down unit=%lf is %lf\n", 0.0,   0.05, RoundUD(false, 0.05, 0.0));
printf("rounding %lf up unit=%lf is %lf\n",   0.01,  0.05, RoundUD(true, 0.05, 0.01));
printf("rounding %lf down unit=%lf is %lf\n", 0.01,  0.05, RoundUD(false, 0.05, 0.01));
printf("rounding %lf up unit=%lf is %lf\n",   -0.01, 0.05, RoundUD(true, 0.05, -0.01));
printf("rounding %lf down unit=%lf is %lf\n", -0.01, 0.05, RoundUD(false, 0.05, -0.01));
printf("%lf has %d d.p.\n", 0.0,     GetDecimalPlaces(0.0));
printf("%lf has %d d.p.\n", 10.0,    GetDecimalPlaces(10.0));
printf("%lf has %d d.p.\n", -10.0,   GetDecimalPlaces(-10.0));
printf("%lf has %d d.p.\n", 10.123,  GetDecimalPlaces(10.123));
printf("%lf has %d d.p.\n", -10.123, GetDecimalPlaces(-10.123));
printf("%lf has %d d.p.\n", 10.01,   GetDecimalPlaces(10.01));
printf("%lf has %d d.p.\n", -10.01,  GetDecimalPlaces(-10.01));
char szFmt[32];
printf(GetFmtStr(szFmt, GetDecimalPlaces(0.0)), 0.0); printf("\n");
printf(GetFmtStr(szFmt, GetDecimalPlaces(10.0)), 10.0); printf("\n");
printf(GetFmtStr(szFmt, GetDecimalPlaces(10.010)), 10.010); printf("\n");
printf(GetFmtStr(szFmt, GetDecimalPlaces(-0.0345)), -0.0345); printf("\n");

The output will be something like:

rounding 10.000000 d.p.=1 is 10.000000
rounding 0.000000 d.p.=0 is 0.000000
rounding 0.100000 d.p.=1 is 0.100000
rounding 0.010000 d.p.=1 is 0.000000
rounding 0.012346 d.p.=0 is 0.000000
rounding 0.012346 d.p.=1 is 0.000000
rounding 0.012346 d.p.=2 is 0.010000
rounding 0.012346 d.p.=6 is 0.012346
rounding -0.012346 d.p.=6 is -0.012346
rounding 0.000000 up unit=0.050000 is 0.000000
rounding 0.000000 down unit=0.050000 is 0.000000
rounding 0.010000 up unit=0.050000 is 0.050000
rounding 0.010000 down unit=0.050000 is 0.000000
rounding -0.010000 up unit=0.050000 is 0.000000
rounding -0.010000 down unit=0.050000 is -0.050000
0.000000 has 0 d.p.
10.000000 has 0 d.p.
-10.000000 has 0 d.p.
10.123000 has 3 d.p.
-10.123000 has 3 d.p.
10.010000 has 2 d.p.
-10.010000 has 2 d.p.
0
10
10.01
-0.0345

Points of Interest

  • Note 1: GetDecimalPlaces() is worth some explanation. In a loop, the double is multiplied by 10 and then compared to its floor value (truncated value). If they are the same, it means we have exhausted the decimal places. Comparison between doubles is tricky because the internal representation of a double is not exact. So, the comparison must allow certain margins of error. The margin is specified by THRES, which is set to 1-10.

History

  • First version: 20 Feb 2008.

License

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