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.
double Round(double dbVal, int nPlaces )
{
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).
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.
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.
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) dbValInUnit = ceil(dbValInUnit);
else 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, 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 double
s 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.