## 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 <span class="code-string">"math.h"</span>

`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.