11,505,744 members (60,777 online)

# Some handy decimal functions for double

, 20 Feb 2008 CPOL 25.2K 22
 Rate this:
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 <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.

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

## Share

Hong Kong
I don't know anything about Lisp, Ruby, D, EJB and J++.

 First Prev Next
 Bug? AvidFan28-Jun-12 1:01 AvidFan 28-Jun-12 1:01
 Re: Bug? Larry Harding24-Jul-12 4:04 Larry Harding 24-Jul-12 4:04
 Floating Point Explorer Mike O'Neill26-Feb-08 13:59 Mike O'Neill 26-Feb-08 13:59
 It's possible that this article fails to articulate the distinction between the stored value of a number and its displayed value. For example, it's not possible to round a floating point number to a designated number of decimal places (except for trivial cases), for the reason that floating point numbers are binary numbers, not decimal numbers. The rounded number will always differ by some small amount from the desired exact result. You can, however, display the number so that it appears to have the exactly correct value, even though it's not. As one quick example, consider the decimal number of 1/10 = 0.10 exactly, in base 10. In binary (base 2), it is simply impossible to express this number exactly, for the reason that it is a repeating binary sequence. In binary, the closest approximation to 1/10 is 0.0001100110011.... , with the "0011" part repeating forever. Of course, even a double can only store 64 bits, so eventually we will need to truncate the repeating "0011" part. And when we truncate, we depart from the desired true value of exactly 1/10 =0.10 (base 10). It should therefore be clear that we can try all we want, but it will never be possible to store a binary number which represents the deciaml value of 0.10 exactly. But with proper formatting options, we can display the stored number so that the displayed value "looks like" the desired result, even though the stored value is different from the displayed value. See Joseph M. Newcomer's "Floating Point Explorer" for one example that attempts to clarify the distinction between a number's stored value and its displayed value: http://www.flounder.com/floating_point_explorer.htm[^]
 Re: Floating Point Explorer S.C.Wong27-Feb-08 15:06 S.C.Wong 27-Feb-08 15:06
 Incidentally ... Mike O'Neill27-Feb-08 15:48 Mike O'Neill 27-Feb-08 15:48
 Last Visit: 31-Dec-99 18:00     Last Update: 3-Jun-15 11:11 Refresh 1