 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Aaron Bentley Guest
|
Posted: Fri Sep 19, 2003 11:03 am Post subject: When does 10 * 7.7 == 76? |
|
|
I've encountered a pretty strange math issue:
#include <iostream>
int main()
{
float num=7.7;
int int_result=static_cast<int>(num*10);
std::cout << int_result << std::endl;
// result: 76
float float_result=num*10;
std::cout << static_cast
// result: 77
}
I seem to get different results depending on whether I store the result
of num*10 in a float or cast it directly into an int.
This happens on gcc 2.96 (I know) and gcc 3.3.1 on x86 (one PIII, one
Duron).
Is this a bug in the compiler or the way it's supposed to behave? And
if it's the way it's supposed to behave, how do I avoid it in the future?
Aaron
--
Aaron Bentley
www.aaronbentley.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Attila Feher Guest
|
Posted: Fri Sep 19, 2003 5:21 pm Post subject: Re: When does 10 * 7.7 == 76? |
|
|
Aaron Bentley wrote:
| Quote: | I've encountered a pretty strange math issue:
#include <iostream
int main()
{
float num=7.7;
int int_result=static_cast
std::cout << int_result << std::endl;
// result: 76
float float_result=num*10;
std::cout << static_cast
// result: 77
}
I seem to get different results depending on whether I store the
result of num*10 in a float or cast it directly into an int.
|
It seems that in the first case you get the computation done using float
(which AFAIK not recommended to be used due to its low precision) while in
the second case you get it done using double precision... But I may be very
wrong.
--
Attila aka WW
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Francis Glassborow Guest
|
Posted: Fri Sep 19, 2003 5:27 pm Post subject: Re: When does 10 * 7.7 == 76? |
|
|
In article <sJrab.2597$Ie5.559749 (AT) news20 (DOT) bellglobal.com>, Aaron Bentley
<aaron.bentley (AT) utoronto (DOT) ca> writes
| Quote: | I've encountered a pretty strange math issue:
|
Actually perfectly normal for floating point computations on a
computer:-)
| Quote: |
#include <iostream
int main()
{
float num=7.7;
int int_result=static_cast
|
num*10 is evaluated in a floating point register which will have more
width that a float store location. The result will be equivalent to
76.99999999...x (exactly how many nines and what x will depend on the
register representation of 7.7*10
| Quote: | std::cout << int_result << std::endl;
// result: 76
float float_result=num*10;
|
This time you have forced the result to be written back and so reduced
the number of bits representing the mantissa. This process is not simple
truncation but tries to store the nearest representable value to that in
the register. The result being equivalent to 77.0000000...y
| Quote: | | std::cout << static_cast
|
which the above line converts to an int before output.
| Quote: | // result: 77
}
I seem to get different results depending on whether I store the result
of num*10 in a float or cast it directly into an int.
|
Yep... that is just one of the many idiosyncrasies resulting from the
way floating point numbers are represented. If your implementation used
a base ten characteristic (which it is allowed to do) it might have just
bumped the characteristic by 1 and you would not have found this
difference. Floating point computations are highly implementation
dependant.
| Quote: |
This happens on gcc 2.96 (I know) and gcc 3.3.1 on x86 (one PIII, one
Duron).
Is this a bug in the compiler or the way it's supposed to behave? And
if it's the way it's supposed to behave, how do I avoid it in the future?
|
--
Francis Glassborow ACCU
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Andrea Griffini Guest
|
Posted: Fri Sep 19, 2003 5:29 pm Post subject: Re: When does 10 * 7.7 == 76? |
|
|
On 19 Sep 2003 07:03:57 -0400, Aaron Bentley
<aaron.bentley (AT) utoronto (DOT) ca> wrote:
| Quote: | Is this a bug in the compiler or the way it's supposed to behave? And
if it's the way it's supposed to behave, how do I avoid it in the future?
|
That's not a bug. One of the two problems you are facing is that
7.7 cannot be represented accurately using a binary format (only
numbers in the form z/(2^n) where z is integer and n a positive
number can be represented without loss of precision).
To a computer using a binary representation 7.7 is very much like
1/3 = 0.33333333... for who works using a decimal representation.
The other problem is that seems you're interested in a very boundary
limit value. For the C++ rules casting a float with value 3.0 to an
int must give 3, but with a value 3.0-whatever must give 2 (no matter
how small can be the positive "whatever").
Normally what is more sensible to do is not having to deal with those
dangerous blade values, and using some reasonable rounding rule.
A way to get there is using the floor function:
int_x = (int)floor(float_x+0.5);
where the conversion to integer is done using a rounding to the
nearest integer value. You still will have "blade" values, but
they will be for example 76.5 and I'm sure you're not going to
question that a number that is somewhat "around" 76.5 because
of an imprecise representation (problem 1) when transformed to
an integer may give either 76 or 77. What surprised you is that
a number that is amazingly close to 77 got approximated to 76.
HTH
Andrea
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ben Hutchings Guest
|
Posted: Fri Sep 19, 2003 10:32 pm Post subject: Re: When does 10 * 7.7 == 76? |
|
|
In article <sJrab.2597$Ie5.559749 (AT) news20 (DOT) bellglobal.com>,
Aaron Bentley wrote:
| Quote: | I've encountered a pretty strange math issue:
|
I'm afraid it's quite normal when dealing with floating-point values.
| Quote: | #include <iostream
int main()
{
float num=7.7;
int int_result=static_cast
std::cout << int_result << std::endl;
// result: 76
float float_result=num*10;
std::cout << static_cast
// result: 77
}
I seem to get different results depending on whether I store the result
of num*10 in a float or cast it directly into an int.
This happens on gcc 2.96 (I know) and gcc 3.3.1 on x86 (one PIII, one
Duron).
Is this a bug in the compiler or the way it's supposed to behave? And
|
Typically the floating-point unit of the processor only supports
one level of precision so all values stored in registers have that
precision.
In the first case (num*10) is probably never stored to memory but
is immediately converted to an int in the FPU. 7.7 cannot be
represented exactly so num*10 turns out as slightly less than 77.
In the latter case (num*10) is stored to memory, losing precision,
and presumably rounding to exactly 77. It is then retrieved for
conversion to int.
Conversion to int always rounds towards zero rather than towards
the nearest integer, so in the first case you get 76 and in the
latter 77.
This is not a bug. This is normal behaviour for floating-point
types - and a good reason to avoid them unless you can accept
this.
| Quote: | if it's the way it's supposed to behave, how do I avoid it in the future?
|
You can get round-to-nearest behaviour by adding or subtracting
0.5 (depending on the sign of the value) before converting to
an integer type.
If you want exact answers, however, use some other type - fixed
point, bignum or rational (none of which are built-in). If you
want slightly more accurate but not exact answers, use double
or long double.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Phil Carmody Guest
|
Posted: Sat Sep 20, 2003 9:58 am Post subject: Re: When does 10 * 7.7 == 76? |
|
|
Aaron Bentley <aaron.bentley (AT) utoronto (DOT) ca> writes:
| Quote: | I've encountered a pretty strange math issue:
|
FP rounding issues. Not so uncommon, even if they are strange.
| Quote: | #include <iostream
int main()
{
float num=7.7;
|
probably stored as the marginally smaller number 16148070*2^-21
| Quote: | | int int_result=static_cast
|
The exact multiplication would yield 10092543.75 * 2^-17, which is
unexpressible exactly as a float.
As a float it would be represented as 10092544*2^-17 (exactly 77)
As a double, it would remain marginally under 77.
If the float were to be cast to an int it would remain 77.
If the double were to be cast to an int it would be rounded down to 76.
| Quote: | std::cout << int_result << std::endl;
// result: 76
|
That must have gone directly from double to int.
| Quote: | float float_result=num*10;
std::cout << static_cast
// result: 77
|
And that must have either gone from double to float to int, or never
been via an intermediate double at all.
| Quote: | }
I seem to get different results depending on whether I store the result
of num*10 in a float or cast it directly into an int.
This happens on gcc 2.96 (I know) and gcc 3.3.1 on x86 (one PIII, one
Duron).
Is this a bug in the compiler or the way it's supposed to behave? And
if it's the way it's supposed to behave, how do I avoid it in the future?
|
I'll let the language lawyers argue semantics, as I've forgotten exactly
what _should_ happen. However, the above hopefully explains what is taking
place (with these compilers at least).
Phil
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Big Brian Guest
|
Posted: Sat Sep 20, 2003 10:02 am Post subject: Re: When does 10 * 7.7 == 76? |
|
|
| Quote: | I've encountered a pretty strange math issue:
#include <iostream
int main()
{
float num=7.7;
int int_result=static_cast
std::cout << int_result << std::endl;
// result: 76
float float_result=num*10;
std::cout << static_cast
// result: 77
}
I seem to get different results depending on whether I store the result
of num*10 in a float or cast it directly into an int.
This happens on gcc 2.96 (I know) and gcc 3.3.1 on x86 (one PIII, one
Duron).
Is this a bug in the compiler or the way it's supposed to behave? And
if it's the way it's supposed to behave, how do I avoid it in the future?
Aaron
|
I get 77 for both with gcc 2.95.2 on HPUX 10.20
-Brian
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Michael Tiomkin Guest
|
Posted: Sat Sep 20, 2003 10:08 am Post subject: Re: When does 10 * 7.7 == 76? |
|
|
Aaron Bentley <aaron.bentley (AT) utoronto (DOT) ca> wrote
| Quote: | I've encountered a pretty strange math issue:
#include <iostream
int main()
{
float num=7.7;
int int_result=static_cast
std::cout << int_result << std::endl;
// result: 76
float float_result=num*10;
std::cout << static_cast
// result: 77
}
......
|
The natural type of a floating point expression/constant is double,
and not float.
This means that in the "int_result" part you first transform 7.7 to float,
then multiply it by 10 as a double, and then transform the result to int.
In the "float_result" you first multipe the double 7.7 by 10, then transform
the result to float, and then transform it to int.
Recall that in binary representation 7.7 cannot be equal to "the real 7.7"
as you know it from school math - 0.1 doesn't have a finite binary
representation.
If transformation from double/float to int on your computer
is done by rounding to 0
or -infinity, then it might happen that
1. (double) 7.7 is greater than the real 7.7 (= 7.7+a, for a very small a)
2. (float) 7.7 is less than the real 7.7 (= 7.7-b, for a small b)
3. the result of your first attempt is 76 (77-10b)
4. the result of your 2nd attempt is 77 (77+10a)
To see what happens on your computer, you can test the following
values:
a) compare (float)7.7 and (double)7.7 The probable result: < or >
If you get equality, something is wrong with your computer: the chain
of binary digits in 7.7 has a small infinite cycle near the start,
and it cannot have a large number of 0s in it (at least 30 for float/double)
b) compare 10*(float)7.7, 77, and 10*7.7. It seems that on your computer
the result is 10*(float)7.7 < 77 < 10*7.7
| Quote: | Is this a bug in the compiler or the way it's supposed to behave? And
if it's the way it's supposed to behave, how do I avoid it in the future?
|
If you know the rounding mode of transformation from double/float
to int, and you know that the value is "almost" int, you can help
making the behaviour more deterministic adding to the result
0.5 for rounding to -infinity, and adding sign(result)*0.5 for rounding
towards 0.
If you really need the exact values of the numbers, you can use
a different representation of them, e.g. as rational numbers etc.
Michael
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Aaron Bentley Guest
|
Posted: Sun Sep 21, 2003 10:12 am Post subject: Re: When does 10 * 7.7 == 76? |
|
|
Thanks. And thanks everyone. I had assumed that truncating a float
would always behave like I expect it to, but the more I think about it,
the more I realize it'd be pretty well impossible.
For this case, I knew I was really dealing with 1 decimal place, so I
didn't consider what I might do in the two-decimal-place case, but it
would have been rounding.
Aaron
--
Aaron Bentley
www.aaronbentley.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ralf Schneeweiß Guest
|
Posted: Sun Sep 21, 2003 10:18 am Post subject: Re: When does 10 * 7.7 == 76? |
|
|
You are right! I tested the following code with VC 6, BC 5.5 an Metrowerks
C++.
In all cases the values has not been equal.
#include <iostream>
int main()
{
float fnum = 7.7;
double dnum = 7.7;
double d1 = fnum; // !!!
double d2 = dnum; // !!!
if( d1 == d2 )
std::cout << "The values are equal." << std::endl;
else
std::cout << "The values are NOT equal." << std::endl;
return 0;
}
Ralf
www.oop-trainer.de
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Francis Glassborow Guest
|
Posted: Sun Sep 21, 2003 6:51 pm Post subject: Re: When does 10 * 7.7 == 76? |
|
|
In article <bkiq25$208vv$1 (AT) ID-204215 (DOT) news.uni-berlin.de>, Ralf
Schneeweiß <ralf.schneeweiss (AT) onlinehome (DOT) de> writes
| Quote: | You are right! I tested the following code with VC 6, BC 5.5 an Metrowerks
C++.
In all cases the values has not been equal.
|
As 7.7 is not exactly representable in binary (nor in any other base
that is not a multiple of ten) your results do not surprise me.
This is one of the reasons why the commercial/financial sector avoids
use of the floating point types. The soon to be proposed (initially to
WG14 -- C) decimal floating point types would guarantee that d1 and d2
compared equal (as they also could on an implementation that used base
ten for its exponent in its representation for float and double)
| Quote: |
#include
int main()
{
float fnum = 7.7;
double dnum = 7.7;
double d1 = fnum; // !!!
double d2 = dnum; // !!!
if( d1 == d2 )
std::cout << "The values are equal." << std::endl;
else
std::cout << "The values are NOT equal." << std::endl;
return 0;
|
--
Francis Glassborow ACCU
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Bronek Kozicki Guest
|
Posted: Mon Sep 22, 2003 7:14 pm Post subject: Re: When does 10 * 7.7 == 76? |
|
|
On 19 Sep 2003 18:32:31 -0400, Ben Hutchings wrote:
| Quote: | If you want exact answers, however, use some other type - fixed
point, bignum or rational (none of which are built-in). If you
|
I know this has question been here before, but I'll try to bring it
again: is there any chance to add anything similar to fixed-point math
to C++ standard language ?
B.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Pete Becker Guest
|
Posted: Tue Sep 23, 2003 11:08 am Post subject: Re: When does 10 * 7.7 == 76? |
|
|
Bronek Kozicki wrote:
| Quote: |
On 19 Sep 2003 18:32:31 -0400, Ben Hutchings wrote:
If you want exact answers, however, use some other type - fixed
point, bignum or rational (none of which are built-in). If you
I know this has question been here before, but I'll try to bring it
again: is there any chance to add anything similar to fixed-point math
to C++ standard language ?
|
Sure. Implement it, get other people to use it, repeat; write up the
result, propose it.
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Roger Willcocks Guest
|
Posted: Tue Sep 23, 2003 6:05 pm Post subject: Re: When does 10 * 7.7 == 76? |
|
|
"Aaron Bentley" <aaron.bentley (AT) utoronto (DOT) ca> wrote
| Quote: | I've encountered a pretty strange math issue:
....
I seem to get different results depending on whether I store the result
of num*10 in a float or cast it directly into an int.
....
--
Aaron Bentley
www.aaronbentley.com
|
Hmm...
#include <iostream>
int main()
{
float num = 7.7;
std::cout << (int)(7.7 * 10) << endl;
std::cout << (int)(num * 10) << endl;
}
compiled with gcc 2.96 this prints 77, 76, but if you optimise with -O3 you
get 77, 77.
That is, the optimiser is precomputing a value differently to that as
calculated at runtime. This, I think, /is/ a bug.
Microsoft's C++ compiler (version 13.10.3077, from Visual Studio .net, the
only other compiler I have to hand) prints 77, 76 whether optimised (with
/Ox) or not. Examining the generated code in the optimised case, you can see
it simply prints the integer constants 77, 76.
--
Roger
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|