Rounding double

J

James Kuyper

Dik said:
Because the return of roundto(6.12345e+50, 4) should be 6.12300e+50.

I think the OP was talking about the number of digits after the decimal
point in %f format, not in %e format.
 
J

James Kuyper

jacob said:
Dik said:
Now, are we really such a C "heads" that we can't answer the OP
question IN HIS terms?

You discarded the solution of James Kuyper out of hand. Did you even
think about that solution?
double roundto(double d,int digits);
double roundto(double d, int digits) {
char number[digits+10];
sprintf(number, "%.*e", (digits > 0 ? digits - 1 : 0), d);
sscanf(number, "%f", &d);
return d;
}

Or somesuch.

But that is just using my function internally within
printf!

No, that's not how printf() has to handle such things. In fact, on an
implementation where long long isn't big enough, printf() can't use your
algorithm, for precisely this reason.
 
B

Bart

double roundto(double d, int digits) {
char number[digits+10];
sprintf(number, "%.*e", (digits > 0 ? digits - 1 : 0), d);
sscanf(number, "%f", &d);
return d;

}

Or somesuch.

That's a hell of way to round a floating point number, to use text
processing routines involving dozens of unnecessary floating point
ops. It's like something out of BASIC.

Rounding such a number need not have anything to do with final display
of the number, and as such may need to be more efficient.

Anyway here's my two cents, rounding to 2 decimal places:

/* this is a code fragment that needs math.h */
double roundcents(double x)
{
double y;

y=floor((fabs(x)+0.005)*100.0)/100.0;

return (x<0.0 ? -y : y);

}

This rounds dollar values to the nearest $0.01, accepting that some
0.01 multiples may be stored approximately. However, taking the /100
out of the code fragment above, the result will be exact (as cents).
The approximation is introduced by the division.

Bart
 
C

CBFalconer

jacob said:
And there are others that do not give ANY solution, limiting
themselves to say why the solutions presented in some cases not
asked for would not work

I used the original solution and fixed it for double precision,
< it is surely not the best solution.
But instead of proposing a better solution this people limit
to talking nonsense without ever proposing anything else.

Then I get angry start getting mad at heathfield and this
degrades.

No, you didn't propose a better solution. You proposed a solution
that is invalid on all the newsgroups posted (long since snipped),
and thoroughly off-topic. You then objected to being corrected.
 
C

CBFalconer

Richard said:
jacob navia said:
.... snip ...


Show me 0.33 rounded to one decimal place.

Hunh? How about:

double r = 0.33, s;

s = (int)((r * 10.0) + 0.5) / 10.0;

which, according to me, gives the desired value in s within the
accuracy limits of doubles. I suspect it might even work for C++.
:)
 
C

CBFalconer

jacob said:
Mark McIntyre wrote:
.... snip ...


Yes, because stdlib.h is missing. You need it for the prototype
of atof. You saw the warning and then you spread nonsense, like
all your posts.

Oh well done. Someone responds to your post, and you then come
back with a nasty dig, thus gaining many friends in the newsgroup.
You really needed that final sentence.
 
R

Richard Heathfield

CBFalconer said:
Hunh? How about:

double r = 0.33, s;

s = (int)((r * 10.0) + 0.5) / 10.0;

which, according to me, gives the desired value in s within the
accuracy limits of doubles.

On my system, it gives 0.299999999999999988898, which doesn't even get the
first digit right.
 
R

Richard Heathfield

Mark McIntyre said:
I copy-pasted this exact code, and compiled under gcc 4.1.2:

thelinux clc_tests $ ./a.out 0.33
1374389535.0000000000000000000: 0 decimals 1374389535.0000000000000000
1374389535.0000000000000000000: 1 decimals 1374389535.0000000000000000

I'm giving this one a 'strange' score of 'high'

Thanks, Mark, for doing that. I used the exact same code, except that I
read ahead in the thread and added <stdlib.h> as a result (I didn't get a
warning about atof because I had to switch off many diagnostics to get the
code to compile at all), and I also had to put braces around the last two
lines of roundto. I'm getting the following results:

me@here> ./foo 0.33
0.3300000000000000155: 0 decimals 256.0000000000000000
0.3300000000000000155: 1 decimals -21474836.0000000000000000
0.3300000000000000155: 2 decimals -20468203.0000000000000000
0.3300000000000000155: 3 decimals -1522532.0000000000000000
0.3300000000000000155: 4 decimals -83414.0000000000000000
0.3300000000000000155: 5 decimals -21322.0000000000000000
0.3300000000000000155: 6 decimals -1591.0000000000000000
0.3300000000000000155: 7 decimals -91.0000000000000000
0.3300000000000000155: 8 decimals 0.0000000000000000
0.3300000000000000155: 9 decimals -1.0000000000000000
0.3300000000000000155: 10 decimals 0.0000000000000000
0.3300000000000000155: 11 decimals 0.0000000000000000
0.3300000000000000155: 12 decimals 0.0000000000000000
0.3300000000000000155: 13 decimals 0.0000000000000000
0.3300000000000000155: 14 decimals 0.0000000000000000
 
R

Richard Heathfield

jacob navia said:

You are saying in essence that 1/10 is not representable in
binary.
Congratulations.

I know that.

You have shown little evidence of this.
But we are speaking about
FLOATING POINT here, that as everyone knows is an approximation
to the reals, and never anything else!

It is clear that "everyone" doesn't include the OP.
Of course there are unrepresentable numbers. Does that mean
that we can't use floating point or what?

No, but it does mean that we can't (in the general case) round a double to
a specified number of decimal digits.
Obviously we can't get any 100% correct representation of
all numbers in floating point, like for instance in base
ten with 1/3!

Which is what we've all been telling the OP, and you've been denying.

So we've been telling him the truth, and you've been trying to mislead him.
Again. As usual, in fact.
If I go to primary school and say to some kid:

Look round me 0.345345345 to 7 places he will tell
me 0.3453453, even if it is obvious that there is a
repeating fraction.

You CUT at some point, as accurately as possible and that was it!

But with floating-point, you *can't* cut in the sense you mean, which is
what we've been telling the OP all along.

Yes.

The existence of unrepresentable numbers in floating point
does not mean we can't use floating point

Strawman. Nobody has said we can't use floating point.
or round numbers as we wish.

We can't round numbers as we wish. The OP wishes to round numbers to a
particular number of decimal places, and this is not possible in the
general case, although of course there are some values for which it will
work.
It means that all our results will be ALWAYS incorrect

Yours? Possibly.
from the mathematically true result by some epsilon
that should be as small as possible.

The OP's requirement as stated was for an epsilon of zero. That is not
possible in the general case, and we told him so.
It is what he asked for.

No, it isn't.
In the original message
there is no mention of 100% accuracy or of any accuracy requirements
in the first place.

Yes, there is. Here is the OP again:

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

A specific number of digits after the decimal place, you see. So an input
of 0.33, 1 should give a result of 0.3 (the *specific* number of digits),
not 0.2999999999999999998 or 0.3000000000000000002, both of which have far
too many decimal places.
I insist that
1) Given the nature of floating point numbers any solution will
be an approximation in most cases.

We have been insisting on that all along, and you've been fighting against
it by claiming that the OP's problem has a solution other than rounding at
the time of conversion to text.
2) We can get the best approximation for double precision by
using more precision internally and then rounding at the
last moment.

Yes, and "the last moment" is at the time of conversion to text, as we've
been saying all along and you've been arguing against.
3) The function proposed is maybe not the best, but it works
for all values in range.

No, it doesn't.
 
P

Philip Potter

jacob said:
I do not know which "C" you are talking about. I am talking about
the C as defined by the C99 standard which says in the
Annex F (normative)

<quote>
The C floating types match the IEC 60559 formats as follows:
— The float type matches the IEC 60559 single format.
— The double type matches the IEC 60559 double format.
<end quote>

That is a quite clearly defined format, defined by IEEE
in 1987 with another name (IEEE754) then generalized to
IEC 60559. This is a 64 bits/53 mantissa etc.

Of course the standard is not a guide for you? Or you are still speaking
about the obsolete 1980 standard?

Which C you are referring to ?

Jacob, this certainly isn't the first time you've been told that IEC
60559 is not a required part of the Standard. I've told you myself before.

Until now, I had considered that while you generate a lot of noise and
are stubborn, you were willing to learn when you saw that you were
wrong. It turns out I was mistaken.

This is most disappointing.
 
P

Philip Potter

jacob said:
Besides, as you know perfectly well, most printfs are
written in C, and they do the same I am doing there
basically.

They're different in a key way, which should be obvious to you but you
are clouding your vision by trying to prove RH wrong rather than trying
to find the truth.

printf() converts a double to characters and rounds there. A character
string representation of a number can have whatever precision it likes,
and more importantly, can use whatever radix it likes. It is trivial to
convert a double to a decimal character string and round that decimal.

What you're trying to do is round a double to the nearest decimal value.
That is not possible without error.
 
R

Richard Heathfield

Philip Potter said:

Jacob, this certainly isn't the first time you've been told that IEC
60559 is not a required part of the Standard. I've told you myself
before.

Until now, I had considered that while you generate a lot of noise and
are stubborn, you were willing to learn when you saw that you were
wrong. It turns out I was mistaken.

This is most disappointing.

If it makes you any less disappointed, at least *someone* in this thread
has learned something. For one thing, I had no idea that the IEC 60559
stuff was in there. For another, having discovered that it was, I had no
idea that it was optional. So I've actually learned *two* things from this
thread, which pleases me greatly. (Whether I remember is another matter,
but I'll do my best.)

I have to say that I was somewhat horrified to learn that ISO appeared to
have put a mandatory upper limit on the precision of doubles, and heartily
relieved to discover that they had not, after all, been so dense as to do
that.
 
P

Philip Potter

Richard said:
Mark McIntyre said:


Thanks, Mark, for doing that. I used the exact same code, except that I
read ahead in the thread and added <stdlib.h> as a result (I didn't get a
warning about atof because I had to switch off many diagnostics to get the
code to compile at all), and I also had to put braces around the last two
lines of roundto. I'm getting the following results:

me@here> ./foo 0.33
0.3300000000000000155: 0 decimals 256.0000000000000000
0.3300000000000000155: 1 decimals -21474836.0000000000000000
0.3300000000000000155: 2 decimals -20468203.0000000000000000
0.3300000000000000155: 3 decimals -1522532.0000000000000000
0.3300000000000000155: 4 decimals -83414.0000000000000000
0.3300000000000000155: 5 decimals -21322.0000000000000000
0.3300000000000000155: 6 decimals -1591.0000000000000000
0.3300000000000000155: 7 decimals -91.0000000000000000
0.3300000000000000155: 8 decimals 0.0000000000000000
0.3300000000000000155: 9 decimals -1.0000000000000000
0.3300000000000000155: 10 decimals 0.0000000000000000
0.3300000000000000155: 11 decimals 0.0000000000000000
0.3300000000000000155: 12 decimals 0.0000000000000000
0.3300000000000000155: 13 decimals 0.0000000000000000
0.3300000000000000155: 14 decimals 0.0000000000000000

Have you any idea why this is happening? I got none of the warnings you
complained about (using -std=c99 -pedantic -W -Wall) and the results
seem to be what Jacob gets. (Of course, if he has invoked UB this means
nothing.)

I do find it ironic that Jacob insists the problem is soluble, but even
after using his rounding he /still/ uses your proposed solution of using
printf() to round on output...

pgp@medusa-s2:~/tmp$ gcc -std=c99 -pedantic -W -Wall -lm jn.c -ojn
jn.c:16: warning: unused parameter 'argc'
pgp@medusa-s2:~/tmp$ ./jn 0.33
0.3300000000000000155: 0 decimals 0.0000000000000000
0.3300000000000000155: 1 decimals 0.3000000000000000
0.3300000000000000155: 2 decimals 0.3300000000000000
0.3300000000000000155: 3 decimals 0.3300000000000000
0.3300000000000000155: 4 decimals 0.3300000000000000
0.3300000000000000155: 5 decimals 0.3300000000000000
0.3300000000000000155: 6 decimals 0.3300000000000000
0.3300000000000000155: 7 decimals 0.3300000000000000
0.3300000000000000155: 8 decimals 0.3300000000000000
0.3300000000000000155: 9 decimals 0.3300000000000000
0.3300000000000000155: 10 decimals 0.3300000000000000
0.3300000000000000155: 11 decimals 0.3300000000000000
0.3300000000000000155: 12 decimals 0.3300000000000000
0.3300000000000000155: 13 decimals 0.3300000000000000
0.3300000000000000155: 14 decimals 0.3300000000000000

I'm disappointed with the misleading results here which suggest that the
LHS has trailing cruft which the RHS doesn't, when both have such cruft.
 
R

Richard Heathfield

Philip Potter said:
Richard Heathfield wrote:
I used the exact same code, except that I
read ahead in the thread and added <stdlib.h> as a result (I didn't get
a warning about atof because I had to switch off many diagnostics to get
the code to compile at all), and I also had to put braces around the
last two lines of roundto. I'm getting the following results:

me@here> ./foo 0.33
0.3300000000000000155: 0 decimals 256.0000000000000000
0.3300000000000000155: 1 decimals -21474836.0000000000000000
0.3300000000000000155: 2 decimals -20468203.0000000000000000
[...]

Have you any idea why this is happening?

Well, obviously I don't have a C99 compiler (I use gcc, which doesn't
conform to C99 yet), so to avoid changing the code too much I am forced to
rely on gcc extensions (which is why I had to turn off my usual collection
of diagnostic switches). It is possible that one or more of the extensions
don't operate according to C99 rules.

I do find it ironic that Jacob insists the problem is soluble, but even
after using his rounding he /still/ uses your proposed solution of using
printf() to round on output...

This rapid accretion of irony over many such threads over several years has
now formed a solid irony cory so large that its gravityy is now having a
significant effect; not only has it attracted a lot of crusty, but it even
appears to have acquired an atmosphery.

I'm disappointed with the misleading results here which suggest that the
LHS has trailing cruft which the RHS doesn't, when both have such cruft.

It's all down to smoke and mirrors.
 
J

jacob navia

Philip said:
Richard Heathfield wrote: [snip]
I'm getting the following results:
me@here> ./foo 0.33
0.3300000000000000155: 0 decimals 256.0000000000000000
0.3300000000000000155: 1 decimals -21474836.0000000000000000
0.3300000000000000155: 2 decimals -20468203.0000000000000000
0.3300000000000000155: 3 decimals -1522532.0000000000000000
0.3300000000000000155: 4 decimals -83414.0000000000000000
0.3300000000000000155: 5 decimals -21322.0000000000000000
0.3300000000000000155: 6 decimals -1591.0000000000000000
0.3300000000000000155: 7 decimals -91.0000000000000000
0.3300000000000000155: 8 decimals 0.0000000000000000
0.3300000000000000155: 9 decimals -1.0000000000000000
0.3300000000000000155: 10 decimals 0.0000000000000000
0.3300000000000000155: 11 decimals 0.0000000000000000
0.3300000000000000155: 12 decimals 0.0000000000000000
0.3300000000000000155: 13 decimals 0.0000000000000000
0.3300000000000000155: 14 decimals 0.0000000000000000

Have you any idea why this is happening? I got none of the warnings you
complained about (using -std=c99 -pedantic -W -Wall) and the results
seem to be what Jacob gets. (Of course, if he has invoked UB this means
nothing.)

I do find it ironic that Jacob insists the problem is soluble, but even
after using his rounding he /still/ uses your proposed solution of using
printf() to round on output...

pgp@medusa-s2:~/tmp$ gcc -std=c99 -pedantic -W -Wall -lm jn.c -ojn
jn.c:16: warning: unused parameter 'argc'
pgp@medusa-s2:~/tmp$ ./jn 0.33
0.3300000000000000155: 0 decimals 0.0000000000000000
0.3300000000000000155: 1 decimals 0.3000000000000000
0.3300000000000000155: 2 decimals 0.3300000000000000
0.3300000000000000155: 3 decimals 0.3300000000000000
0.3300000000000000155: 4 decimals 0.3300000000000000
0.3300000000000000155: 5 decimals 0.3300000000000000
0.3300000000000000155: 6 decimals 0.3300000000000000
0.3300000000000000155: 7 decimals 0.3300000000000000
0.3300000000000000155: 8 decimals 0.3300000000000000
0.3300000000000000155: 9 decimals 0.3300000000000000
0.3300000000000000155: 10 decimals 0.3300000000000000
0.3300000000000000155: 11 decimals 0.3300000000000000
0.3300000000000000155: 12 decimals 0.3300000000000000
0.3300000000000000155: 13 decimals 0.3300000000000000
0.3300000000000000155: 14 decimals 0.3300000000000000

I'm disappointed with the misleading results here which suggest that the
LHS has trailing cruft which the RHS doesn't, when both have such cruft.

1: This is because I forgot in the format specifier of the LHS to
imit to DBL_DIG.
2: As you can see, my solution works in your machine. Either RH is not
telling the truth (unlikely) or he is using some minor error in the
code to trip about.
3: It is interesting to note that you say that my solution doesn't
work when you see correct results in your machine.
 
R

Richard Heathfield

jacob navia said:

2: As you can see, my solution works in your machine. Either RH is not
telling the truth (unlikely) or he is using some minor error in the
code to trip about.

If your code has an error, however minor, then it is broken, in which case
I suggest you post a fixed version. The version you posted *does not work*
on my system. Quite apart from the fact that you are trying to solve an
impossible problem (the problem, remember, is that of rounding the value
of a double to a specified number of decimal places, which simply can't be
done), you are trying to solve it in a way that produces bizarrely
incorrect results on at least one system.

And no, I didn't make those results up.
 
J

jacob navia

Richard said:
jacob navia said:



If your code has an error, however minor, then it is broken, in which case
I suggest you post a fixed version. The version you posted *does not work*
on my system. Quite apart from the fact that you are trying to solve an
impossible problem (the problem, remember, is that of rounding the value
of a double to a specified number of decimal places, which simply can't be
done), you are trying to solve it in a way that produces bizarrely
incorrect results on at least one system.

And no, I didn't make those results up.

Since you provide no information on which compilation options
you used, no information on which system you are running, this
can't be solved really.

And if I have a minor error somewhere correct it instead
of just spreading FUD
 
R

Richard

jacob navia said:
Since you provide no information on which compilation options
you used, no information on which system you are running, this
can't be solved really.

And if I have a minor error somewhere correct it instead
of just spreading FUD

So that would be the "minor error in the code to trip about" option from
RH then. Good to see nothing much changes with regard to his superiority
complex.
 
R

Richard Heathfield

jacob navia said:

Since you provide no information on which compilation options
you used, no information on which system you are running, this
can't be solved really.

The compilation options shouldn't matter, but I'll tell you anyway:

gcc -o foo foo.c -lm

If the system matters, then presumably you agree that your code is not
portable C, and specify on which systems or classes of system you expect
your code to work. But I'll tell you anyway: AMD Athlon XP1600+ running at
1.4GHz.
And if I have a minor error somewhere correct it instead
of just spreading FUD

The correction is easy:

rm foo.c

del foo.c

....or whatever local command does the equivalent (i.e. delete the source
file).

I recommend this correction to the OP, since the program you posted
completely fails to solve the original problem that he posed. It can't be
corrected for the simple reason that the problem as stated cannot be
solved, as I have already pointed out several times in this thread.

And no, I'm not spreading FUD. Fear doesn't enter into it, and there is no
doubt, no uncertainty, whatsoever that your code does *not* solve the
problem stated by the OP. It attempts to solve a different problem, and
does so in a non-portable manner (because otherwise I'd get the same
results that you do).
 

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,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top