Some handy decimal functions for double
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).
#include "math.h"
Round()
rounds a double to a certain number of decimal places. It is very similar to Excel's Round function.
// 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).
// 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.
// 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.
// 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:
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, thedouble
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 betweendouble
s is tricky because the internal representation of adouble
is not exact. So, the comparison must allow certain margins of error. The margin is specified byTHRES
, which is set to 1-10.
History
- First version: 20 Feb 2008.