Rounding double

M

md

Hi

Does any body know, how to round a double value with a specific number
of digits after the decimal points?

A function like this:

RoundMyDouble (double &value, short numberOfPrecisions)

It then updates the value with numberOfPrecisions after the decimal
point.

Any help is appreciated.

Thanks.
md
 
S

suresh

Hi

Does any body know, how to round a double value with a specific number
of digits after the decimal points?

A function like this:

RoundMyDouble (double &value, short numberOfPrecisions)
{
int p = pow(10, numberOfPrecisions);
value = (int)(value * p + 0.5) / (double)p;
}
 
D

David Harmon

On Wed, 21 Nov 2007 21:39:57 -0800 (PST) in comp.lang.c++, md
Hi

Does any body know, how to round a double value with a specific number
of digits after the decimal points?

A function like this:

RoundMyDouble (double &value, short numberOfPrecisions)

You cannot in general round a double value to a specific number of
digits, because doubles are not decimal. Save your rounding 'til you
are ready to format the number into a decimal string.

See "What Every Computer Scientist Should Know About Floating-Point
Arithmetic" http://docs.sun.com/source/806-3568/ncg_goldberg.html
 
R

Richard Heathfield

md said:
Hi

Does any body know, how to round a double value with a specific number
of digits after the decimal points?

A function like this:

RoundMyDouble (double &value, short numberOfPrecisions)

It then updates the value with numberOfPrecisions after the decimal
point.

Well, you have a syntax error right there: double &value isn't legal. You
presumably meant double *value.

Elsethread, you were given this suggestion (suitably modified so that it
will actually compile, and with a driver added):

#include <stdio.h>
#include <math.h>

void RoundMyDouble (double *value, short numberOfPrecisions)

{
int p = pow(10, numberOfPrecisions);
*value = (int)(*value * p + 0.5) / (double)p;
}

int main(void)
{
double v = 3.14159265358979323846;
short int ndp = 0;
while(ndp < 10)
{
double newv = v;
RoundMyDouble(&newv, ndp);
printf("%.16f, \"rounded\" to %hd decimals, is %.16f\n",
v,
ndp++,
newv);
}

return 0;
}

Here are the test results:

3.1415926535897931, "rounded" to 0 decimals, is 3.0000000000000000
3.1415926535897931, "rounded" to 1 decimals, is 3.1000000000000001
3.1415926535897931, "rounded" to 2 decimals, is 3.1400000000000001
3.1415926535897931, "rounded" to 3 decimals, is 3.1419999999999999
3.1415926535897931, "rounded" to 4 decimals, is 3.1415999999999999
3.1415926535897931, "rounded" to 5 decimals, is 3.1415899999999999
3.1415926535897931, "rounded" to 6 decimals, is 3.1415929999999999
3.1415926535897931, "rounded" to 7 decimals, is 3.1415926999999999
3.1415926535897931, "rounded" to 8 decimals, is 3.1415926500000002
3.1415926535897931, "rounded" to 9 decimals, is -2.1474836480000001

As you can see, it doesn't really round at all. It nudges the value close
to what is required, but doesn't hit the requirement right on the nose.
And that last result looks like a lot of fun, doesn't it? :)
 
J

Jim Langston

Richard Heathfield said:
md said:


Well, you have a syntax error right there: double &value isn't legal. You
presumably meant double *value.

Actually, double& value is legal in C++ but not C. It declares value as a
non-constant reference. It is not legal in C however. This was cross posted
to two newsgroups (c.l.c++ and c.l.c) which is usually a bad idea just for
this problem.

[snip rest of reply]
 
R

Richard Heathfield

Jim Langston said:
Actually, double& value is legal in C++ but not C.

Right - but of course a cross-posted article should "work" in all the
groups into which it's posted. Here, the post worked in clc++ but not in
clc, so either it should not have been posted to clc or the pointer syntax
should have been used rather than the reference syntax (at which point, of
course, there would have been howls of protest from the clc++ crowd, and
perhaps rightly so).
This was cross
posted to two newsgroups (c.l.c++ and c.l.c) which is usually a bad idea
just for this problem.

Indeed.
 
M

md

md said:






Well, you have a syntax error right there: double &value isn't legal. You
presumably meant double *value.

Elsethread, you were given this suggestion (suitably modified so that it
will actually compile, and with a driver added):

#include <stdio.h>
#include <math.h>

void RoundMyDouble (double *value, short numberOfPrecisions)

{
int p = pow(10, numberOfPrecisions);
*value = (int)(*value * p + 0.5) / (double)p;

}

int main(void)
{
double v = 3.14159265358979323846;
short int ndp = 0;
while(ndp < 10)
{
double newv = v;
RoundMyDouble(&newv, ndp);
printf("%.16f, \"rounded\" to %hd decimals, is %.16f\n",
v,
ndp++,
newv);
}

return 0;

}

Here are the test results:

3.1415926535897931, "rounded" to 0 decimals, is 3.0000000000000000
3.1415926535897931, "rounded" to 1 decimals, is 3.1000000000000001
3.1415926535897931, "rounded" to 2 decimals, is 3.1400000000000001
3.1415926535897931, "rounded" to 3 decimals, is 3.1419999999999999
3.1415926535897931, "rounded" to 4 decimals, is 3.1415999999999999
3.1415926535897931, "rounded" to 5 decimals, is 3.1415899999999999
3.1415926535897931, "rounded" to 6 decimals, is 3.1415929999999999
3.1415926535897931, "rounded" to 7 decimals, is 3.1415926999999999
3.1415926535897931, "rounded" to 8 decimals, is 3.1415926500000002
3.1415926535897931, "rounded" to 9 decimals, is -2.1474836480000001

As you can see, it doesn't really round at all. It nudges the value close
to what is required, but doesn't hit the requirement right on the nose.
And that last result looks like a lot of fun, doesn't it? :)

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

Yes, Richard. That is the result I also got.
Sorry for sending the message to two different groups.

Can some one give a solution for how to solve this problem?

Thanks again
md
 
M

md

On Wed, 21 Nov 2007 21:39:57 -0800 (PST) in comp.lang.c++, md





You cannot in general round a double value to a specific number of
digits, because doubles are not decimal. Save your rounding 'til you
are ready to format the number into a decimal string.

See "What Every Computer Scientist Should Know About Floating-Point
Arithmetic" http://docs.sun.com/source/806-3568/ncg_goldberg.htm

What do you mean David? Is ther a solution for this?
Thanks.

Regards,
md
 
M

md

{
int p = pow(10, numberOfPrecisions);
value = (int)(value * p + 0.5) / (double)p;



}- Hide quoted text -

- Show quoted text -

I tried that before but it does not work!

md
 
R

Richard Heathfield

md said:
Yes, Richard. That is the result I also got.
Sorry for sending the message to two different groups.

Can some one give a solution for how to solve this problem?

It has already been explained to you that this simply can't be done (in
general). Let's take a frinstance, and pi/10 is as good as anything else
for this purpose:

0.314159265358989323846...

and you want to round it to 6 decimal places. So you want the value of the
double to be precisely 0.314159.

Let's see how we might represent such a number, using pure binary notation.
(Floating point numbers aren't actually stored in pure binary, but it's
not all /that/ different, it's the same underlying problem, and this one
is easier to explain and easier to follow.)

In pure binary notation, we use a binary point rather than a decimal point
("radix point" is an umbrella term that covers them both, and others). The
column to the immediate left of the binary point is worth 1. Every column
to the right of the binary point is worth half as much as its immediate
left neighbour. Thus, 0.5 (decimal) is 0.1 (binary), 0.25 (decimal) is
0.01 (binary), 0.75 (decimal) is 0.11 (binary), and so on.

So how do we represent EXACTLY 0.314159 in pure binary notation? How many
bits do you think we'll need?

0.1 = 1/2 = 0.5 - too large
0.01 = 1/4 = 0.25 - too small
0.011 = 3/8 = 0.375 - too large
0.0101 = 5/16 = 0.3125 - too small
0.01011 = 11/32 = 0.34375 - too large
0.010101 = 21/64 = 0.328125 - too large
0.0101001 = 41/128 = 0.3203125 - too large
0.01010001 = 81/256 = 0.31640625 - too large
0.010100001 = 161/512 = 0.314453125 - too large
0.0101000001 = 321/1024 = 0.3134765625 - too small
0.01010000011 = 643/2048 = 0.31396484375 - too small
0.010100000111 = 1287/4096 = 0.314208984375 - too large
0.0101000001101 = 2573/8192 = 0.3140869140625 - too small
0.01010000011011 = 5147/16384 = 0.31414794921875 - too small
0.010100000110111 = 10295/32768 = 0.314178466796875 - too large
0.0101000001101101 = 20589/65536 = 0.3141632080078125 - too large

Sixteen bits so far, and we're not even close.

So instead of trying to do the impossible, just round the displayed value
rather than the stored value. For example, if you want to display pi/10 to
six places, just do this: printf("%.6f\n", pi/10);
 
J

jacob navia

md said:
I tried that before but it does not work!

md

This can go beyond 9:
#include <stdio.h>
#include <math.h>

void RoundMyDouble (long double *value, short numberOfPrecisions)

{
long long p = powl(10.0L, numberOfPrecisions);
*value = (long long)(*value * p + 0.5L) / (double)p;
}

int main(void)
{
long double v = 3.1415926535897932384626L;
short int ndp = 0;
while(ndp < 20)
{
long double newv = v;
RoundMyDouble(&newv, ndp);
printf("%.21Lf, \"rounded\" to %hd decimals, is %.21Lf\n",
v,
ndp++,
newv);
}

return 0;
}

You will need a modern C compiler to use this (C99 standard).
Using lcc-win I get:
 
R

Richard Heathfield

jacob navia said:

#include <stdio.h>
#include <math.h>

void RoundMyDouble (long double *value, short numberOfPrecisions)

{
long long p = powl(10.0L, numberOfPrecisions);
*value = (long long)(*value * p + 0.5L) / (double)p;
}

int main(void)
{
long double v = 3.1415926535897932384626L;
short int ndp = 0;
while(ndp < 20)
{
long double newv = v;
RoundMyDouble(&newv, ndp);
printf("%.21Lf, \"rounded\" to %hd decimals, is %.21Lf\n",
v,
ndp++,
newv);
}

return 0;
}

You will need a modern C compiler to use this (C99 standard).
Using lcc-win I get:

Do you think that's a compiler bug, or a programmer bug?
 
J

jacob navia

Richard said:
jacob navia said:



Do you think that's a compiler bug, or a programmer bug?

output of gcc:
3.1415926535897932385: 0 decimals 3.0000000000000000000
[snip]
3.1415926535897932385: 17 decimals 3.1415926535897932400
3.1415926535897932385: 18 decimals 3.1415926535897932389
3.1415926535897932385: 19 decimals 1.0000000000000000000

If it is a compiler bug, it is a shared compiler bug.

It isn't a programmer bug either. Long long has a finite
number of decimal digits as you may know.

18.
 
M

Marco Manfredini

jacob said:
This can go beyond 9:
#include <stdio.h>
#include <math.h>

void RoundMyDouble (long double *value, short numberOfPrecisions)

{
long long p = powl(10.0L, numberOfPrecisions);
*value = (long long)(*value * p + 0.5L) / (double)p;
}

I have floor(). It even goes past 11!
 
J

James Kuyper

jacob said:
Richard said:
jacob navia said:




Do you think that's a compiler bug, or a programmer bug?

output of gcc:
3.1415926535897932385: 0 decimals 3.0000000000000000000
[snip]
3.1415926535897932385: 17 decimals 3.1415926535897932400
3.1415926535897932385: 18 decimals 3.1415926535897932389
3.1415926535897932385: 19 decimals 1.0000000000000000000

If it is a compiler bug, it is a shared compiler bug.

It isn't a programmer bug either. Long long has a finite
number of decimal digits as you may know.

Which makes use of long long a programming error. You should have used
one of the rounding functions like floorl(), nearbyintl(), rintl(), or
roundl(), instead of conversion to long long. Even with that fix, the
algorithm you choose needlessly results in overflow (or underflow, for
negative values of numberOfPrecisions) in some cases.

I say that it's needless (and therefore a programming error) because you
can avoid both problems by using sprintf() and sscanf(). There are more
efficient ways of doing it, but that one is sufficient.
 
D

David Harmon

On Thu, 22 Nov 2007 09:12:02 +0000 in comp.lang.c++, Richard
Heathfield said:
Right - but of course a cross-posted article should "work" in all the
groups into which it's posted.

But saying that "double &value" is illegal doesn't "work" in c.l.c++
 
J

jacob navia

James said:
jacob said:
Richard said:
jacob navia said:

<snip>

#include <stdio.h>
#include <math.h>

void RoundMyDouble (long double *value, short numberOfPrecisions)

{
long long p = powl(10.0L, numberOfPrecisions);
*value = (long long)(*value * p + 0.5L) / (double)p;
}

int main(void)
{
long double v = 3.1415926535897932384626L;
short int ndp = 0;
while(ndp < 20)
{
long double newv = v;
RoundMyDouble(&newv, ndp);
printf("%.21Lf, \"rounded\" to %hd decimals, is %.21Lf\n",
v,
ndp++,
newv);
}

return 0;
}

You will need a modern C compiler to use this (C99 standard).
Using lcc-win I get:

<snip>

3.1415926535897932390: 19 decimals 1.0000000000000000000

Do you think that's a compiler bug, or a programmer bug?

output of gcc:
3.1415926535897932385: 0 decimals 3.0000000000000000000
[snip]
3.1415926535897932385: 17 decimals 3.1415926535897932400
3.1415926535897932385: 18 decimals 3.1415926535897932389
3.1415926535897932385: 19 decimals 1.0000000000000000000

If it is a compiler bug, it is a shared compiler bug.

It isn't a programmer bug either. Long long has a finite
number of decimal digits as you may know.

Which makes use of long long a programming error.

Why?
I get 18 digits, and this is more than enough for double precision
that gives 16 digits at best!
You should have used
one of the rounding functions like floorl(), nearbyintl(), rintl(), or
roundl(), instead of conversion to long long. Even with that fix, the
algorithm you choose needlessly results in overflow (or underflow, for
negative values of numberOfPrecisions) in some cases.

I did not choose any algorithm. It was proposed by somebody else,
and I merely adapted it to give enough decimal digits for
double precision. Note that I did NOT change the cast in the
denominator of the division from double to long double.

I say that it's needless (and therefore a programming error) because you
can avoid both problems by using sprintf() and sscanf(). There are more
efficient ways of doing it, but that one is sufficient.

And this one is sufficient for double precision too.
 
J

James Kuyper

jacob said:
James Kuyper wrote: ....

Why?
I get 18 digits, and this is more than enough for double precision
that gives 16 digits at best!

That assertion depends upons unportable (and more importantly,
unnecessary) assumptions about the width of long long and the precision
of long double.
 
J

jacob navia

James said:
That assertion depends upons unportable (and more importantly,
unnecessary) assumptions about the width of long long and the precision
of long double.

Probably. Your solution is always the best since you did not
propose any

:)
 
M

Marco Manfredini

jacob said:
Why?
I get 18 digits, and this is more than enough for double precision
that gives 16 digits at best!

I'm curious. What does your machine print for 6.0221415e23 ?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,228
Members
46,817
Latest member
AdalbertoT

Latest Threads

Top