Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C Programming (http://www.velocityreviews.com/forums/f42-c-programming.html)
-   -   Rounding a floating point number (http://www.velocityreviews.com/forums/t594003-rounding-a-floating-point-number.html)

 jacob navia 02-25-2008 10:18 AM

Rounding a floating point number

Hi

"How can I round a number to x decimal places" ?

This question keeps appearing. I would propose the following
solution

#include <float.h>
#include <math.h>

double roundto(double x, int digits)
{
int sgn=1;

if (x == 0)
return 0;
if (digits > DBL_DIG)
digits = DBL_DIG;
else if (digits < 1)
digits = 1;

if(x < 0.0) {
sgn = -sgn;
x = -x;
}
double p = floorl(log10l(x));
p--;
p = digits-p;
double pow10 = pow(10.0, p);
return sgn*floor(x*pow10+0.5)/pow10;
}

long double roundtol(long double x, int digits)
{
int sgn=1;

if (x == 0)
return 0;
if (digits > LDBL_DIG)
digits = LDBL_DIG;
else if (digits < 1)
digits = 1;

if(x < 0.0) {
sgn = -sgn;
x = -x;
}
long double p = floorl(log10l(x));
p--;
p = digits-p;
long double pow10 = powl(10.0, p);
return sgn*floorl(x*pow10+0.5)/pow10;
}
#include <stdio.h>
int main(void)
{
double d = 1.7888889988996678;
long double ld = 1.7888889988996678998877L;

for (int i = 0; i<=DBL_DIG;i++) {
printf("i=%d: %.15g\n",i,roundto(d,i));
}
printf("\n 1.7888889988996678\n");
for (int i = 0; i<=LDBL_DIG;i++) {
printf("i=%d: %.18Lg\n",i,roundtol(ld,i));
}
printf("\n 1.7888889988996678998877L\n");
return 0;
}

--------------------------------------------------------------------
I would propose it to add it to the FAQ.

--
jacob navia
jacob at jacob point remcomp point fr
logiciels/informatique
http://www.cs.virginia.edu/~lcc-win32

 Richard Heathfield 02-25-2008 10:28 AM

Re: Rounding a floating point number

jacob navia said:

> Hi
>
> "How can I round a number to x decimal places" ?
>
> This question keeps appearing.

<snip>

> I would propose it to add it to the FAQ.

Good idea. You could call it Question 14.6.

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999

 Bartc 02-25-2008 11:02 AM

Re: Rounding a floating point number

"Richard Heathfield" <rjh@see.sig.invalid> wrote in message
news:xcednVO6muLICl_anZ2dneKdnZydnZ2d@bt.com...
> jacob navia said:
>
>> Hi
>>
>> "How can I round a number to x decimal places" ?
>>
>> This question keeps appearing.

>
> <snip>
>
>> I would propose it to add it to the FAQ.

>
> Good idea. You could call it Question 14.6.

14.6 is already used by How to Round a Number [to an integer]

--
Bart

 jacob navia 02-25-2008 11:19 AM

Re: Rounding a floating point number

Richard Heathfield wrote:
> jacob navia said:
>
>> Hi
>>
>> "How can I round a number to x decimal places" ?
>>
>> This question keeps appearing.

>
> <snip>
>
>> I would propose it to add it to the FAQ.

>
> Good idea. You could call it Question 14.6.
>

Your kill file is not working apparently
:-)

That question addresses the rounding to nearest integer.
This answers the question of rounding to a number of decimal places

Not the same thing. But yes, we could add this to 14.6

--
jacob navia
jacob at jacob point remcomp point fr
logiciels/informatique
http://www.cs.virginia.edu/~lcc-win32

 MisterE 02-25-2008 11:24 AM

Re: Rounding a floating point number

> 14.6 is already used by How to Round a Number [to an integer]

Then what need is there for this question? Its already answered in the faq.
Multiply, round then divide.

 Richard Heathfield 02-25-2008 11:35 AM

Re: Rounding a floating point number

Bartc said:

>
> "Richard Heathfield" <rjh@see.sig.invalid> wrote in message
> news:xcednVO6muLICl_anZ2dneKdnZydnZ2d@bt.com...
>> jacob navia said:
>>
>>> Hi
>>>
>>> "How can I round a number to x decimal places" ?
>>>
>>> This question keeps appearing.

>>
>> <snip>
>>
>>> I would propose it to add it to the FAQ.

>>
>> Good idea. You could call it Question 14.6.

>
> 14.6 is already used by How to Round a Number [to an integer]

From the FAQ, Q14.6: "You can round to a certain precision by scaling:

(int)(x / precision + 0.5) * precision"

(int)(3.141 / 0.01 + 0.5) * 0.01 gives us:

(int)(314.1 + 0.5) * 0.01 which is

(int)314.6 * 0.01 which is

314 * 0.01 which is

3.14

QED

(Of course, the usual caveats about floating point apply here.)

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999

 Martien Verbruggen 02-25-2008 11:38 AM

Re: Rounding a floating point number

On Mon, 25 Feb 2008 12:19:35 +0100,
jacob navia <jacob@nospam.com> wrote:
> Richard Heathfield wrote:
>> jacob navia said:
>>
>>> Hi
>>>
>>> "How can I round a number to x decimal places" ?
>>>
>>> This question keeps appearing.

>>
>> <snip>
>>
>>> I would propose it to add it to the FAQ.

>>
>> Good idea. You could call it Question 14.6.
>>

> That question addresses the rounding to nearest integer.
> This answers the question of rounding to a number of decimal places

It also answers the question of how to round to a certain number of
digits:

\begin{quote}
You can round to a certain precision by scaling:
\end{quote}

Martien
--
|
Martien Verbruggen | prepBut nI vrbLike adjHungarian! qWhat's
| artThe adjBig nProblem? -- Alec Flett
|

 Bartc 02-25-2008 12:19 PM

Re: Rounding a floating point number

"Richard Heathfield" <rjh@see.sig.invalid> wrote in message
news:oZGdneAOqMSCOl_aRVnyhAA@bt.com...
> Bartc said:
>
>>
>> "Richard Heathfield" <rjh@see.sig.invalid> wrote in message
>> news:xcednVO6muLICl_anZ2dneKdnZydnZ2d@bt.com...
>>> jacob navia said:
>>>
>>>> Hi
>>>>
>>>> "How can I round a number to x decimal places" ?
>>>>
>>>> This question keeps appearing.
>>>
>>> <snip>
>>>
>>>> I would propose it to add it to the FAQ.
>>>
>>> Good idea. You could call it Question 14.6.

>>
>> 14.6 is already used by How to Round a Number [to an integer]

>
> From the FAQ, Q14.6: "You can round to a certain precision by scaling:
>
> (int)(x / precision + 0.5) * precision"
>
> (int)(3.141 / 0.01 + 0.5) * 0.01 gives us:
>
> (int)(314.1 + 0.5) * 0.01 which is
>
> (int)314.6 * 0.01 which is
>
> 314 * 0.01 which is
>
> 3.14

Yeah, you're right. Perhaps 14.6 should be written more clearly, with regard
to what exactly it means by Precision. And an example added, like yours.

>(int)(3.141 / 0.01 + 0.5) * 0.01

Somehow I would be happier if that was written as:

>(int)(3.141 *100 + 0.5) /100

Then any errors due to 0.01 not being exactly 1/100 are someone else's
fault.

--
Bart

 Mark McIntyre 02-25-2008 04:05 PM

Re: Rounding a floating point number

MisterE wrote:
>> 14.6 is already used by How to Round a Number [to an integer]

>
> Then what need is there for this question?

<Fx: swoosh sound of clue going over head>

--
Mark McIntyre

CLC FAQ <http://c-faq.com/>

 Keith Thompson 02-25-2008 05:43 PM

Re: Rounding a floating point number

jacob navia <jacob@nospam.com> writes:
> "How can I round a number to x decimal places" ?
>
> This question keeps appearing. I would propose the following
> solution

Yes, the question keeps appearing. The best response, IMHO, is to ask
the questioner why he wants to do that.

> #include <float.h>
> #include <math.h>
>
> double roundto(double x, int digits)
> {
> int sgn=1;
>
> if (x == 0)
> return 0;
> if (digits > DBL_DIG)
> digits = DBL_DIG;
> else if (digits < 1)
> digits = 1;

Silently changing any digits'' value less than 1 to 1 seems like a
bad idea. For example, rounding to 0 digits is perfectly sensible;
roundto(1.234, 0) should return 1.0. For that matter,
roundto(123456.0, -2) should probably return 123400.0.

I haven't thought much about whether values greater than DBL_DIG might
make sense in some cases. You might just return the value of x
directly.

> if(x < 0.0) {
> sgn = -sgn;
> x = -x;
> }
> double p = floorl(log10l(x));

Why do you use floorl and log10l in both roundto and roundtol?

It's worth mentioning that floorl and long10l are new in C99, and may
not be supported by all implementations.

A solution not using transcendental functions *might* be faster.

> p--;
> p = digits-p;
> double pow10 = pow(10.0, p);

You're also mixing declarations and statements, which is perfectly
legal in C99, but may not be supported by all compilers. If this were
to be added to the FAQ, either this should be mentioned or all
declarations should be moved to the beginning of the block.

It's very possible that computing 10**p by repeated multiplication is
going to be faster than calling pow(), which is a transcendental
function. (It's also possible that some pow() implementations
optimize the case where the second argument is equal to an integer
value; I haven't checked.)

It would be nice if the standard library provided a variant of the
pow() function whose second parameter is an integer (type int is

> return sgn*floor(x*pow10+0.5)/pow10;
> }
>
> long double roundtol(long double x, int digits)

[similar code snipped]

> #include <stdio.h>
> int main(void)
> {
> double d = 1.7888889988996678;
> long double ld = 1.7888889988996678998877L;
>
> for (int i = 0; i<=DBL_DIG;i++) {
> printf("i=%d: %.15g\n",i,roundto(d,i));
> }
> printf("\n 1.7888889988996678\n");
> for (int i = 0; i<=LDBL_DIG;i++) {
> printf("i=%d: %.18Lg\n",i,roundtol(ld,i));
> }
> printf("\n 1.7888889988996678998877L\n");
> return 0;
> }
>
> --------------------------------------------------------------------
> I would propose it to add it to the FAQ.

FAQ 14.6 proposes

(int)(x / precision + 0.5) * precision

Your roundto(x, 2) should be equivalent to the above with
precision==0.01. On the other hand, the FAQ's solution fails when (x
/ precision + 0.5) is outside the range of type int.

I compiled and ran your program and got:

i=0: 1.79
i=1: 1.79
i=2: 1.789
i=3: 1.7889
[...]

I've already pointed out your odd treatment of i=0. I think you have
an off-by-one error as well.

Finally, I question the usefulness of this entire approach (and the
following is something that probably should be added to the FAQ).
(And didn't we discuss this at some length a while ago?)

To take a concrete example, rounding the value 1.2345 to 2 decimal
places cannot be done exactly in binary floating-point. On one
system, the actual stored value for 1.23 is
1.229999999999999982236431605997495353221893310546 875
Certainly most of those digits are not significant; the point is that
1.23 cannot be stored exactly, and that in this case it's
mathematically less than 1.23.

What is the purpose of rounding a floating-point value to N decimal
places? 99% of the time[*], the only purpose is to provide a textual
representation of the number. Storing an approximation of the rounded
value in a floating-point representation is not the best way to
accomplish that. Just pass the original *unrounded* value to printf
(or fprintf, or sprintf) with an appropriate format:

printf("%.2f\n", 1.2345);

Attempting to convert 1.2345 to a floating-point representation that
closely approximates the value 1.23 merely wastes CPU cyles and
introduces more possiblities for error.

The question of rounding a floating-point number to a specified number
of decimal places is an opportunity to teach the questioner a thing or
two about how floating-point arithmetic works. By accepting the
premise of the question, you pass up this opportunity.

There may well be cases where the kind of internal rounding you
describe is useful (perhaps in financial calculations). In such
cases, it might make more sense to store the value in fixed-point (a
scaled integer), or to keep the unrounded value in a structure along
with an integer specifying the rounding to be applied later. Some,
but by not means all, financial problems can be solved by storing the
number of cents rather than the number of dollars (or euros, or
whatever). In the remaining cases where you really need a
floating-point approximation of the rounded value, it's likely that a
specific algorithm is required by financial regulations.

In however many cases remain after that, either the solution proposed
in the FAQ or your solution (with corrections) could be useful.

If you can present a non-artificial case where computing an
approximate floating-point rounded result, rather than doing the
rounding on output, is actually required, I'd be interested in seeing
it.
[*] Yes, I made up the 99% figure, but it's not an unreasonable guess.

--
Keith Thompson (The_Other_Keith) <kst-u@mib.org>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

All times are GMT. The time now is 12:36 AM.