By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,009 Members | 2,866 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,009 IT Pros & Developers. It's quick & easy.

Unwanted rounding

P: n/a
Hello,

I have :
float f = 36.09999999;

When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);

I get : 36.100

How could I get 36.099 ?

Thanks in advance.
Dec 1 '06 #1
Share this Question
Share on Google+
29 Replies


P: n/a
Marco <.said:
Hello,

I have :
float f = 36.09999999;
I recommend double rather than float.
>
When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);
Not lf - just f.
>
I get : 36.100

How could I get 36.099 ?
Remove the precision specification (03), and then search the string for the
decimal point, using strchr. Check that you have at least three valid
characters (non-'\0') after the decimal point, and make the fourth one '\0'
to truncate the string at the point you want.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 1 '06 #2

P: n/a

Marco wrote:
Hello,

I have :
float f = 36.09999999;

When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);

I get : 36.100

How could I get 36.099 ?
You can't. See http://c-faq.com/fp/printfprec.html

Dec 1 '06 #3

P: n/a
ma**********@pobox.com said:
>
Marco wrote:
>Hello,

I have :
float f = 36.09999999;

When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);

I get : 36.100

How could I get 36.099 ?

You can't. See http://c-faq.com/fp/printfprec.html
He can. See my parallel reply.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 1 '06 #4

P: n/a

Richard Heathfield wrote:
ma**********@pobox.com said:

Marco wrote:
Hello,

I have :
float f = 36.09999999;

When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);

I get : 36.100

How could I get 36.099 ?
You can't. See http://c-faq.com/fp/printfprec.html

He can. See my parallel reply.
OOPS! Mea Culpa. Thanks

Dec 1 '06 #5

P: n/a
Richard Heathfield wrote:
Remove the precision specification (03), and then search the string for the
decimal point, using strchr. Check that you have at least three valid
characters (non-'\0') after the decimal point, and make the fourth one '\0'
to truncate the string at the point you want.
It doesn't work (double, "%f")
I read floating representation and I understood it can't work...
Dec 1 '06 #6

P: n/a
Marco <.said:
Richard Heathfield wrote:
>Remove the precision specification (03), and then search the string for
the decimal point, using strchr. Check that you have at least three valid
characters (non-'\0') after the decimal point, and make the fourth one
'\0' to truncate the string at the point you want.

It doesn't work (double, "%f")
I read floating representation and I understood it can't work...
I think I understand what may be happening. When I first implemented a
solution for you, I took your existing code, which used float, and did
this:

#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main(void)
{
float f = 36.0999999;
char cf[25] = {0};
int i = 0;
char *p = NULL;
sprintf(cf, "%f", f);
printf("[%s]\n", cf);
p = strchr(cf, '.');
if(p != NULL)
{
++p;
while(i < 3 && isdigit(*p))
{
++i;
++p;
}
*p = '\0';
}
printf("[%s]\n", cf);
return 0;
}

This is what I got as output:

[36.099998]
[36.099]

which is obviously correct. But if I follow my own suggestion and substitute
float with double, I get this output:

[36.100000]
[36.100]

Obviously not what you want! So I modified this line:

sprintf(cf, "%f", f);

to this:

sprintf(cf, "%.16f", f);

with this result:

[36.0999999000000003]
[36.099]

which *is* what you want.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 1 '06 #7

P: n/a
On Fri, 01 Dec 2006 10:54:43 +0100, wrote:
Hello,

I have :
float f = 36.09999999;

When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);

I get : 36.100

How could I get 36.099 ?

Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char** argv)
{
float f = 36.09999;
printf( "%.3f\n", ((int)(f*1e3))*1e-3);
return EXIT_SUCCESS;
}
Converting floats to integers doesn't cause rounding.
Duncan
Dec 1 '06 #8

P: n/a
Duncan Muirhead said:
float f = 36.09999;
printf( "%.3f\n", ((int)(f*1e3))*1e-3);
That's much simpler than mine, which is good - but alas I do have a nit to
pick with it: if f exceeds INT_MAX / 1000, which might be as low as 32, you
have an obvious problem.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 1 '06 #9

P: n/a
Richard Heathfield wrote:
with this result:

[36.0999999000000003]
[36.099]

which *is* what you want.
But it works just by chance :-)
If I set 10.0899999, it doesn't work...
Dec 1 '06 #10

P: n/a
Marco <.said:
Richard Heathfield wrote:
>with this result:

[36.0999999000000003]
[36.099]

which *is* what you want.

But it works just by chance :-)
If I set 10.0899999, it doesn't work...
But if you follow my suggestion of using double (and sprintf(cf, "%.16f",
f)), it works fine:

[10.0899999000000005]
[10.089]

Is that not the result you expected?

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 1 '06 #11

P: n/a
Richard Heathfield wrote:
But if you follow my suggestion of using double (and sprintf(cf, "%.16f",
f)), it works fine:

[10.0899999000000005]
[10.089]

Is that not the result you expected?
For this case, yes.
But if I have : 13.99999999999999999999999999, I get 14.000000..........
even with "%.40f"
Dec 1 '06 #12

P: n/a
Marco <.said:
Richard Heathfield wrote:
>But if you follow my suggestion of using double (and sprintf(cf, "%.16f",
f)), it works fine:

[10.0899999000000005]
[10.089]

Is that not the result you expected?

For this case, yes.
But if I have : 13.99999999999999999999999999, I get 14.000000..........
even with "%.40f"
Good. Now we're getting somewhere. And at *this* point, we have to ask how
much precision you are expecting from your floating-point values. Let's
look at a pure binary representation of 13.99999999999999999999999999.

We start off with 13, which is easy: 1101. Four bits so far.

Then comes the binary point. We'll give you that for free. :-)

Now all we have to do is represent 0.99999999999999999999999999 in binary.
Since the number is so close to 1, we can approximate rapidly via (n-1)/n
where n is a power of 2.

1/2 is too low.
3/4 is too low.
7/8 is too low.
15/16 is too low.
31/32 is too low.
....cutting to the chase...
1023/1024 is too low...
65535/65536 is too low - so 16 bits isn't enough...
....and we eventually discover that we need to go as high as
154742504910672534362390527 / 154742504910672534362390528

That's 87 bits of precision. Plus those four for your 13, making 91 bits of
precision altogether. Do you have a double that big?

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 1 '06 #13

P: n/a
On Fri, 01 Dec 2006 11:25:07 +0000, Richard Heathfield wrote:
Duncan Muirhead said:
>float f = 36.09999;
printf( "%.3f\n", ((int)(f*1e3))*1e-3);

That's much simpler than mine, which is good - but alas I do have a nit to
pick with it: if f exceeds INT_MAX / 1000, which might be as low as 32, you
have an obvious problem.
Fair point. How about

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main( int argc, char** argv)
{
float f = 36.09999;
printf( "%.3f\n", f - fmod(f, 1e-3));
return EXIT_SUCCESS;
}
Duncan

Dec 1 '06 #14

P: n/a
Duncan Muirhead said:
On Fri, 01 Dec 2006 11:25:07 +0000, Richard Heathfield wrote:
>Duncan Muirhead said:
>>float f = 36.09999;
printf( "%.3f\n", ((int)(f*1e3))*1e-3);

That's much simpler than mine, which is good - but alas I do have a nit
to pick with it: if f exceeds INT_MAX / 1000, which might be as low as
32, you have an obvious problem.

Fair point. How about

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main( int argc, char** argv)
{
float f = 36.09999;
printf( "%.3f\n", f - fmod(f, 1e-3));
return EXIT_SUCCESS;
}
Again, far simpler than mine, and I can't think of any nits.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 1 '06 #15

P: n/a

Marco wrote:
Hello,

I have :
float f = 36.09999999;

When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);

I get : 36.100

How could I get 36.099 ?

Thanks in advance.

This falls squarely into the catagory of
"if you have to ask, you shouldn't be doing this".
Why would you prefer 36.099 rather than 36.100
as a three decimal place representation of 36.09999999?

- William Hughes

Dec 1 '06 #16

P: n/a
William Hughes wrote:
Marco wrote:
Hello,

I have :
float f = 36.09999999;

When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);

I get : 36.100

How could I get 36.099 ?

Thanks in advance.


This falls squarely into the catagory of
"if you have to ask, you shouldn't be doing this".
What a silly rule. So, no one should ever learn anything by asking
about something they don't know about?
Why would you prefer 36.099 rather than 36.100
as a three decimal place representation of 36.09999999?
It's probable that the OP's simply curious as to this behaviour. Once
he learns the underlying reason he may very well go on to simply use
36.100

Dec 1 '06 #17

P: n/a

santosh wrote:
William Hughes wrote:
Marco wrote:
Hello,
>
I have :
float f = 36.09999999;
>
When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);
>
I get : 36.100
>
How could I get 36.099 ?
>
Thanks in advance.

This falls squarely into the catagory of
"if you have to ask, you shouldn't be doing this".

What a silly rule. So, no one should ever learn anything by asking
about something they don't know about?
No. But there is a big difference between "Why do
I see this behaviour?" and "How can I change this
behaviour?". It is the second question to which

"if you have to ask, you shouldn't be doing this"

applies.

- William Hughes

Dec 1 '06 #18

P: n/a
Marco wrote:
Hello,

I have :
float f = 36.09999999;

When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);

I get : 36.100

How could I get 36.099 ?

Thanks in advance.
You are aware that 0.9... = 1 aren't you?
Just wanted to make sure...

/Mattan
Ref: http://www.faqs.org/faqs/sci-math-fa...bers/0.999eq1/
Dec 1 '06 #19

P: n/a
>I have :
>float f = 36.09999999;
There is no exact representation of 36.09999999 in binary floating point.

36.0999999999 as long double:
Before: 36.09999999989999999727707802321674535050988197326 660156250000000000000000000000000
Value: 36.09999999990000000074652497517035953933373093605 041503906250000000000000000000000
After: 36.09999999990000000421597192712397372815757989883 422851562500000000000000000000000

36.0999999999 as double:
Before: 36.09999999989999253102723741903901100158691406250 0000000000000
Value: 36.09999999989999963645459502004086971282958984375 0000000000000
After: 36.09999999990000674188195262104272842407226562500 0000000000000

36.0999999999 as float:
Before: 36.09999465942382812500000000000000000000000000000 0000000000000
Value: 36.09999847412109375000000000000000000000000000000 0000000000000
After: 36.10000228881835937500000000000000000000000000000 0000000000000
>When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);

I get : 36.100
There is no exact representation of 36.100 in binary floating point.

36.100 as long double:
Before: 36.09999999999999999514277426726494013564661145210 266113281250000000000000000000000
Value: 36.09999999999999999861222121921855432447046041488 647460937500000000000000000000000
After: 36.10000000000000000208166817117216851329430937767 028808593750000000000000000000000

36.100 as double:
Before: 36.09999999999999431565811391919851303100585937500 0000000000000
Value: 36.10000000000000142108547152020037174224853515625 0000000000000
After: 36.10000000000000852651282912120223045349121093750 0000000000000

36.100 as float:
Before: 36.09999465942382812500000000000000000000000000000 0000000000000
Value: 36.09999847412109375000000000000000000000000000000 0000000000000
After: 36.10000228881835937500000000000000000000000000000 0000000000000
>
How could I get 36.099 ?
There is no exact representation of 36.099 in binary floating point.

36.099 as long double:
Before: 36.09899999999999999772404279951842909213155508041 381835937500000000000000000000000
Value: 36.09900000000000000119348975147204328095540404319 763183593750000000000000000000000
After: 36.09900000000000000466293670342565746977925300598 144531250000000000000000000000000

36.099 as double:
Before: 36.09899999999998954081092961132526397705078125000 0000000000000
Value: 36.09899999999999664623828721232712268829345703125 0000000000000
After: 36.09900000000000375166564481332898139953613281250 0000000000000

36.099 as float:
Before: 36.09899520874023437500000000000000000000000000000 0000000000000
Value: 36.09899902343750000000000000000000000000000000000 0000000000000
After: 36.09900283813476562500000000000000000000000000000 0000000000000
>
Thanks in advance.

Dec 2 '06 #20

P: n/a
Gordon Burditt wrote:
>I have :
float f = 36.09999999;

There is no exact representation of 36.09999999 in binary floating point.

36.0999999999 as long double:
Before: 36.09999999989999999727707802321674535050988197326 660156250000000000000000000000000
Value: 36.09999999990000000074652497517035953933373093605 041503906250000000000000000000000
After: 36.09999999990000000421597192712397372815757989883 422851562500000000000000000000000

36.0999999999 as double:
Before: 36.09999999989999253102723741903901100158691406250 0000000000000
Value: 36.09999999989999963645459502004086971282958984375 0000000000000
After: 36.09999999990000674188195262104272842407226562500 0000000000000

36.0999999999 as float:
Before: 36.09999465942382812500000000000000000000000000000 0000000000000
Value: 36.09999847412109375000000000000000000000000000000 0000000000000
After: 36.10000228881835937500000000000000000000000000000 0000000000000
>When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);

I get : 36.100

There is no exact representation of 36.100 in binary floating point.

36.100 as long double:
Before: 36.09999999999999999514277426726494013564661145210 266113281250000000000000000000000
Value: 36.09999999999999999861222121921855432447046041488 647460937500000000000000000000000
After: 36.10000000000000000208166817117216851329430937767 028808593750000000000000000000000

36.100 as double:
Before: 36.09999999999999431565811391919851303100585937500 0000000000000
Value: 36.10000000000000142108547152020037174224853515625 0000000000000
After: 36.10000000000000852651282912120223045349121093750 0000000000000

36.100 as float:
Before: 36.09999465942382812500000000000000000000000000000 0000000000000
Value: 36.09999847412109375000000000000000000000000000000 0000000000000
After: 36.10000228881835937500000000000000000000000000000 0000000000000
>How could I get 36.099 ?

There is no exact representation of 36.099 in binary floating point.

36.099 as long double:
Before: 36.09899999999999999772404279951842909213155508041 381835937500000000000000000000000
Value: 36.09900000000000000119348975147204328095540404319 763183593750000000000000000000000
After: 36.09900000000000000466293670342565746977925300598 144531250000000000000000000000000

36.099 as double:
Before: 36.09899999999998954081092961132526397705078125000 0000000000000
Value: 36.09899999999999664623828721232712268829345703125 0000000000000
After: 36.09900000000000375166564481332898139953613281250 0000000000000

36.099 as float:
Before: 36.09899520874023437500000000000000000000000000000 0000000000000
Value: 36.09899902343750000000000000000000000000000000000 0000000000000
After: 36.09900283813476562500000000000000000000000000000 0000000000000
>Thanks in advance.

Given our ubiquitous 64-bit IEEE double (53 mantissa bits)
36.099 as double has no precision beyond
3.6098999999999997e+01
That printf("%.60f", 36.099) can give you something like
36.09899999999999664623828721232712268829300000000 0000000000000
might tease you to believe you have precision to 40+ digits. You don't.

--
Joe Wright
"Everything should be made as simple as possible, but not simpler."
--- Albert Einstein ---
Dec 2 '06 #21

P: n/a
>>I have :
>>float f = 36.09999999;

There is no exact representation of 36.09999999 in binary floating point.

36.0999999999 as long double:
Before:
36.0999999998999999972770780232167453505098819732 6660156250000000000000000000000000
>Value:
36.0999999999000000007465249751703595393337309360 5041503906250000000000000000000000
>After:
36.0999999999000000042159719271239737281575798988 3422851562500000000000000000000000
>>
36.0999999999 as double:
Before: 36.09999999989999253102723741903901100158691406250 0000000000000
Value: 36.09999999989999963645459502004086971282958984375 0000000000000
After: 36.09999999990000674188195262104272842407226562500 0000000000000

36.0999999999 as float:
Before: 36.09999465942382812500000000000000000000000000000 0000000000000
Value: 36.09999847412109375000000000000000000000000000000 0000000000000
After: 36.10000228881835937500000000000000000000000000000 0000000000000
>>When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);

I get : 36.100

There is no exact representation of 36.100 in binary floating point.

36.100 as long double:
Before:
36.0999999999999999951427742672649401356466114521 0266113281250000000000000000000000
>Value:
36.0999999999999999986122212192185543244704604148 8647460937500000000000000000000000
>After:
36.1000000000000000020816681711721685132943093776 7028808593750000000000000000000000
>>
36.100 as double:
Before: 36.09999999999999431565811391919851303100585937500 0000000000000
Value: 36.10000000000000142108547152020037174224853515625 0000000000000
After: 36.10000000000000852651282912120223045349121093750 0000000000000

36.100 as float:
Before: 36.09999465942382812500000000000000000000000000000 0000000000000
Value: 36.09999847412109375000000000000000000000000000000 0000000000000
After: 36.10000228881835937500000000000000000000000000000 0000000000000
>>How could I get 36.099 ?

There is no exact representation of 36.099 in binary floating point.

36.099 as long double:
Before:
36.0989999999999999977240427995184290921315550804 1381835937500000000000000000000000
>Value:
36.0990000000000000011934897514720432809554040431 9763183593750000000000000000000000
>After:
36.0990000000000000046629367034256574697792530059 8144531250000000000000000000000000
>>
36.099 as double:
Before: 36.09899999999998954081092961132526397705078125000 0000000000000
Value: 36.09899999999999664623828721232712268829345703125 0000000000000
After: 36.09900000000000375166564481332898139953613281250 0000000000000

36.099 as float:
Before: 36.09899520874023437500000000000000000000000000000 0000000000000
Value: 36.09899902343750000000000000000000000000000000000 0000000000000
After: 36.09900283813476562500000000000000000000000000000 0000000000000
>>Thanks in advance.

Given our ubiquitous 64-bit IEEE double (53 mantissa bits)
36.099 as double has no precision beyond
3.6098999999999997e+01
That printf("%.60f", 36.099) can give you something like
36.0989999999999966462382872123271226882930000000 00000000000000
might tease you to believe you have precision to 40+ digits. You don't.
Since the value given above doesn't end in 5 followed by trailing
zeroes, and it's not an exact integer, your example won't happen
unless printf() is introducing unwanted rounding.

The point of the output is that you have three consecutive floating
point numbers (with no intermediate values in between) so rounding
decimal numbers to put them in floating-point variables is inevitable
and will result in errors.

A floating-point variable contains a number (except when it's NaN
or Inf or some such thing) and it is perfectly possible and reasonable
to print out *EXACTLY* what that value is, to infinite precision,
particularly when investigating problems of unwanted precision loss
or comparing what you got with what you should have gotten if
everything was done in infinite-precision math.

Now, if that number 36.099 represents the weight in kilograms of
something, you are correct that it is highly unlikely to have
anywhere near 17 digits of precision in the result.

Dec 2 '06 #22

P: n/a
Gordon Burditt wrote:
[ snip ]
>Given our ubiquitous 64-bit IEEE double (53 mantissa bits)
36.099 as double has no precision beyond
3.6098999999999997e+01
That printf("%.60f", 36.099) can give you something like
36.0989999999999966462382872123271226882930000000 00000000000000
might tease you to believe you have precision to 40+ digits. You don't.

Since the value given above doesn't end in 5 followed by trailing
zeroes, and it's not an exact integer, your example won't happen
unless printf() is introducing unwanted rounding.
Where is (at what position) printf introducing this rounding?
The point of the output is that you have three consecutive floating
point numbers (with no intermediate values in between) so rounding
decimal numbers to put them in floating-point variables is inevitable
and will result in errors.
I'm at a loss here. I have no idea what you mean.
A floating-point variable contains a number (except when it's NaN
or Inf or some such thing) and it is perfectly possible and reasonable
to print out *EXACTLY* what that value is, to infinite precision,
particularly when investigating problems of unwanted precision loss
or comparing what you got with what you should have gotten if
everything was done in infinite-precision math.
A floating point variable (double, let's say) can hold a value precise
to approximately 17 decimal digits. Nothing infinite about it.
Now, if that number 36.099 represents the weight in kilograms of
something, you are correct that it is highly unlikely to have
anywhere near 17 digits of precision in the result.
Why kilograms? The double has 53 bits and about 17 digits of precision
no matter whether its value is kilos, nanos or light years. Using printf
and friends to show decimal digits beyond 17 or so is misleading.

--
Joe Wright
"Everything should be made as simple as possible, but not simpler."
--- Albert Einstein ---
Dec 3 '06 #23

P: n/a
>>Given our ubiquitous 64-bit IEEE double (53 mantissa bits)
>>36.099 as double has no precision beyond
3.6098999999999997e+01
That printf("%.60f", 36.099) can give you something like
36.098999999999996646238287212327122688293000000 000000000000000
might tease you to believe you have precision to 40+ digits. You don't.

Since the value given above doesn't end in 5 followed by trailing
zeroes, and it's not an exact integer, your example won't happen
unless printf() is introducing unwanted rounding.
Where is (at what position) printf introducing this rounding?

36.098999999999997 as double:
Before: 36.09899999999998954081092961132526397705078125000 0000000000000
Value: 36.09899999999999664623828721232712268829345703125 0000000000000
After: 36.09900000000000375166564481332898139953613281250 0000000000000

Assuming that this is stored in a IEEE 64-bit floating point number,
the rounding is 4 places in the 16th digit to the left of the decimal point.
The actual number needs to be one of the three listed above, or something
even farther away, since there aren't any numbers between the Before: and
Value: numbers or between the Value: and After: numbers.

Value: 36.09899999999999664623828721232712268829345703125 0000000000000
Input: 36.09899999999999700000000000000000000000000000000 0000000000000
^^
>The point of the output is that you have three consecutive floating
point numbers (with no intermediate values in between) so rounding
decimal numbers to put them in floating-point variables is inevitable
and will result in errors.
I'm at a loss here. I have no idea what you mean.
The program prints three consecutive floating point numbers.
There's no numbers in between them at the specified precision.
If you want to represent something close to the one in the middle,
you've got these three choices. Anything else is further away.
>A floating-point variable contains a number (except when it's NaN
or Inf or some such thing) and it is perfectly possible and reasonable
to print out *EXACTLY* what that value is, to infinite precision,
particularly when investigating problems of unwanted precision loss
or comparing what you got with what you should have gotten if
everything was done in infinite-precision math.
A floating point variable (double, let's say) can hold a value precise
to approximately 17 decimal digits. Nothing infinite about it.
When you convert a floating point number (say, double) to decimal,
it may take many more digits than 17 to represent EXACTLY the value
it represents.
>Now, if that number 36.099 represents the weight in kilograms of
something, you are correct that it is highly unlikely to have
anywhere near 17 digits of precision in the result.
Why kilograms? The double has 53 bits and about 17 digits of precision
no matter whether its value is kilos, nanos or light years. Using printf
and friends to show decimal digits beyond 17 or so is misleading.
It's not misleading to represent the exact value of a floating-point
number in decimal when discussing rounding error and the limits of
precision of various types.

Dec 4 '06 #24

P: n/a
In article <5P******************************@comcast.comJoe Wright <jo********@comcast.netwrites:
Gordon Burditt wrote:
[ snip ]
Given our ubiquitous 64-bit IEEE double (53 mantissa bits)
36.099 as double has no precision beyond
3.6098999999999997e+01
That printf("%.60f", 36.099) can give you something like
36.09899999999999664623828721232712268829300000000 0000000000000
might tease you to believe you have precision to 40+ digits. You don't.
Since the value given above doesn't end in 5 followed by trailing
zeroes, and it's not an exact integer, your example won't happen
unless printf() is introducing unwanted rounding.
Where is (at what position) printf introducing this rounding?
A floating point number is (by definition) a number of the form
m * base^exp
where m and exp are integer (the possibility that m is a fraction
can be ignored because it can be made integer by suitable change
of the exponent) and base is the base of the representation,
which is 2 in IEEE. So a floating point number is in essence a
rational number. If the base contains only prime factors 2 and/or
5, the denumerator of that number is a divisor of a power of 10,
and so the number has an exact representation in finite decimal
notation. So if printf is giving the above representation it is
doing some rounding, because that is not the exact representation
of an IEEE floating point number.
The point of the output is that you have three consecutive floating
point numbers (with no intermediate values in between) so rounding
decimal numbers to put them in floating-point variables is inevitable
and will result in errors.
I'm at a loss here. I have no idea what you mean.
Given some number in decimal notation there is either a single
floating point number that it matches, or there are two floating
point numbers, one of them larger and one of them smaller than
the number given. 36.099 does not have an exact representation,
so there are two numbers, one larger and one smaller.
A floating-point variable contains a number (except when it's NaN
or Inf or some such thing) and it is perfectly possible and reasonable
to print out *EXACTLY* what that value is, to infinite precision,
particularly when investigating problems of unwanted precision loss
or comparing what you got with what you should have gotten if
everything was done in infinite-precision math.
A floating point variable (double, let's say) can hold a value precise
to approximately 17 decimal digits. Nothing infinite about it.
Each floating point number is exactly representable in decimal notation.
Again, nothing infinite in it.
Now, if that number 36.099 represents the weight in kilograms of
something, you are correct that it is highly unlikely to have
anywhere near 17 digits of precision in the result.
Why kilograms? The double has 53 bits and about 17 digits of precision
no matter whether its value is kilos, nanos or light years. Using printf
and friends to show decimal digits beyond 17 or so is misleading.
That may be quite something else. But showing exact representations
to show that 36.099 is not representable as a floating point number is
not misleading at all.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Dec 4 '06 #25

P: n/a
"Dik T. Winter" wrote:
Joe Wright <jo********@comcast.netwrites:
>Gordon Burditt wrote:
.... snip ...
>>
Where is (at what position) printf introducing this rounding?

A floating point number is (by definition) a number of the form
m * base^exp
where m and exp are integer (the possibility that m is a fraction
can be ignored because it can be made integer by suitable change
of the exponent) and base is the base of the representation,
which is 2 in IEEE. So a floating point number is in essence a
rational number. If the base contains only prime factors 2 and/or
5, the denumerator of that number is a divisor of a power of 10,
and so the number has an exact representation in finite decimal
notation. So if printf is giving the above representation it is
doing some rounding, because that is not the exact representation
of an IEEE floating point number.
You neglect that the usual base is 2, and that 10 is only used for
input/output translation to/from text format.
>>The point of the output is that you have three consecutive
floating point numbers (with no intermediate values in between)
so rounding decimal numbers to put them in floating-point
variables is inevitable and will result in errors.

I'm at a loss here. I have no idea what you mean.

Given some number in decimal notation there is either a single
floating point number that it matches, or there are two floating
point numbers, one of them larger and one of them smaller than
the number given. 36.099 does not have an exact representation,
so there are two numbers, one larger and one smaller.
Since in general we cannot make an exact equivalent between
representation as (2 ** binexp) and (10 ** decexp) the two
representations cannot be exact equivalents (outside of a few
specific values).

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
Dec 4 '06 #26

P: n/a
Duncan Muirhead wrote:

[suggesting a way to print 36.09999999 as "36.099" instead of "36.100"]
float f = 36.09999;
printf( "%.3f\n", f - fmod(f, 1e-3));
Try this with 31.0999999.

Or 0.9.

See my longer post downthread for more details.

- Ernie http://home.comcast.net/~erniew

Dec 4 '06 #27

P: n/a
CBFalconer wrote:
Since in general we cannot make an exact equivalent between
representation as (2 ** binexp) and (10 ** decexp) the two
representations cannot be exact equivalents (outside of a few
specific values).
You might want to give the phrasing of this some more thought.

We aren't comparing logarithms, which is what binexp and decexp are.
The question would be whether a number represented as

(1 + m / 2^b) * 2^e

with e, m, b integers, can be written exactly (with a finite number of
digits) as a number in base 10, and as it happens, *all* of them can.

Every one of the numbers that can be represented in IEEE 754, by far the
most common floating-point encoding, can also be represented exactly in
base 10, because 10 contains 2 as a factor. It's only in the other
direction that we have an issue.

You can convince yourself of this by looking at the decimal expansion
for various values of binexp.

-1 0.5
-2 0.25
-3 0.125
-4 0.0625
-5 0.03125
...
-23 0.00000011920928955078125

To create the decimal expansion of a binary fraction with b binary
digits, you just need to add up the numbers on the right for each
corresponding 1 bit in the binary fraction. This'll produce a decimal
expansion with at most b decimal digits.

For floats, b = 23, and for doubles, b = 53.

The problems arise in the other direction.

The OP had asked how to use printf() to display

float f = 36.09999999;

as "36.099" rather than "36.100". More generally, he wants rounding
toward zero, rather than rounding to nearest.

Someone suggested using

printf( "%.3f\n", f - fmod( f, 1e-3 ));

This looks like a good approach, but it can only touch the rounding done
in printf(). The compiler must still round the decimal value of f in
order to binary-encode it as a float, and it'll always do this using
nearest-value rounding, so there are some values that will be rounded up
rather than down.

An example is 31.0999999f. This actually has one fewer 9 than the OP's
example value, but the compiler will round it up to 31.1 before it ever
reaches printf().

31.0999999f can't be represented exactly, so the compiler has to choose
between the two nearest values that it *can* represent exactly,

(1 + 7916748 / 8388608) * 16 = 31.09999847412109375
(1 + 7916749 / 8388608) * 16 = 31.1000003814697265625

and it turns out the second one is closer.

If you think this is just a problem of having too many digits, consider
what happens to 0.9. It can't be represented exactly either, and

float f = 0.9f;
printf( "%.3f\n", f - fmod( f, 1e-3 ));

prints "0.899", probably not what the OP wanted.

The only surefire way to handle this is not to allow any rounding that
you don't control. In particular, you can't store numbers in base 2.
You have to maintain them as a string of decimal digits, and you have to
perform all of the arithmetic in decimal.

- Ernie http://home.comcast.net/~erniew

Dec 4 '06 #28

P: n/a

Richard Heathfield skrev:
Marco <.said:
Hello,

I have :
float f = 36.09999999;

I recommend double rather than float.

When I do :
char cf[25];
sprintf(cf,"%0.03lf", f);

Not lf - just f.

I get : 36.100

How could I get 36.099 ?

Remove the precision specification (03), and then search the string for the
decimal point, using strchr. Check that you have at least three valid
characters (non-'\0') after the decimal point, and make the fourth one '\0'
to truncate the string at the point you want.
how about..

sprintf(cf,"%0.03lf", floor(f*1000)/1000);

-Lasse

Dec 4 '06 #29

P: n/a
la******@ieee.org wrote:
how about..

sprintf(cf,"%0.03lf", floor(f*1000)/1000);
Try it with 31.0999999f or 0.9f.

- Ernie http://home.comcast.net/~erniew

Dec 4 '06 #30

This discussion thread is closed

Replies have been disabled for this discussion.