Equality test and accuracy of machine [duplicate] - c

This question already has answers here:
Floating point inaccuracy examples
(7 answers)
C++ floating point precision [duplicate]
(5 answers)
Closed 8 years ago.
I found this code snippet on Page 174, A Book on C -Al Kelley, Ira Pohl .
int main()
{
int cnt=0; double sum=0.0,x;
for( x=0.0 ;x!= 9.9 ;x+=0.1)
{
sum=sum +x;
printf("cnt = %5d\n",cnt++);
}
return 0;
}
and it became a infinite loop as the book said it would. It didnt mention the precise reason except saying that it had to do with the accuracy of the machine.
I modified the code to check if
x=9.9
would ever become true, i.e. x was attaining 9.9 by adding the following lines
diff=x-9.9;
printf("cnt =10%d \a x =%10.10lf dif=%10.10lf \n",++cnt,x,diff);
and i got the following lines among the output
cnt =1098 x =9.7000000000 dif=-0.2000000000
cnt =1099 x =9.8000000000 dif=-0.1000000000
cnt =10100 x =9.9000000000 dif=-0.0000000000
cnt =10101 x =10.0000000000 dif=0.1000000000
cnt =10102 x =10.1000000000 dif=0.2000000000
if x is attaining the value 9.9 exactly , why is it still a infinite loop?

You are simply printing the number with too poor accuracy to notice that it isn't exact. Try something like this:
#include <stdio.h>
int main()
{
double d = 9.9;
if(d == 9.9)
{
printf("Equal!");
}
else
{
printf("Not equal! %.20f", d);
}
}
Output on my machine:
Not equal! 9.90000000000000035527
The book is likely trying to teach you to never use == or != operators to compare floating point variables. Also for the same reason, never use floats as loop iterators.

The problem is that most floating point implementation are based on IEEE 754. See http://en.wikipedia.org/wiki/IEEE_floating_point
The problem with this is, that numbers are builded with base 2 (binary formats).
The number 9.9 can never be build with base 2 excatly.
The "Numerical Computation Guide" by David Goldberg gves an exact statement about it:
Several different representations of real numbers have been proposed,
but by far the most widely used is the floating-point representation.
Floating-point representations have a base b (which is always assumed to
be even) and a precision p. If b = 10 and p = 3, then the number 0.1 is
represented as 1.00 × 10^-1. If b = 2 and p = 24, then the decimal
number 0.1 cannot be represented exactly, but is approximately
1.10011001100110011001101 × 2^-4.

You can safely assume two floating point numbers are never equal 'exactly' (unless one is a copy of the other).

Computer works on binary and floating point, in other words in base 2. Just like base 10, base 2 have numbers that it cannot build. For example, try to write the fraction 10/3 in base 10. You'll end up with infinite 3s. and in Binary, you cannot even write 0.1 (decimal) in binary, you'll also get a recurring pattern 0.0001100110011... (binary).
This video will do better to explain http://www.youtube.com/watch?v=PZRI1IfStY0

Related

I think the below program should atleast run once as the condition is true or maybe i am getting it wrong. Please enlighten me [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 1 year ago.
a very simple program but i can't figure out why it prints nothing. it should atleast run once.
int main()
{
float x = 1.1 ;
while ( x == 1.1 )
{
printf ( "%f\n", x ) ;
x = x-0.1 ;
}
return 0 ;
}
The decimal fraction 1.1 cannot be represented exactly using binary floating-point numbers (just like fraction 1/3 cannot be represented exactly using decimal floating-point numbers).
When you attempt to put 1.1 in type double, what you really store is 1.100000000000000088817841970012523233890533447265625
When you attempt to put 1.1 in type float, what you really store is 1.10000002384185791015625
Those values are not equal, so "x == 1.1" is false!
To make your program work, either use double
or use float:
````float x=1.1f; while (x==1.1f) ...
but don’t mix the types

equality of two float numbers in for loop [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 5 years ago.
What I am trying to do here is, I am taking a precision value (less than 1) and printing count of all the numbers of type 1/n (n is natural number) which are greater than or equal to the precision value (input).
#include <stdio.h>
int main(){
int i=1,terms=0;
float n;
printf("Enter precision value : ");
scanf("%f",&n);
while(i>0){
if ((1.0/i) >= n){
printf("%f\n",1.0/i);
sum = sum + (1.0/i);
terms++;
i++;
}
else
break;
}
printf("number of terms : %d\n",terms);
return 0;
}
But if I give input 0.1 than the count (output) is showing only 9. but it should show 10 (it is not including 1/10).
I was using a for loop before and I know that breaking a loop with an if-else statement is not the best thing.
That's just the way math works with limited precision. For example, say you're using six digits of decimal precision. The best you can do for one-third is 0.333333 and the best you can do for 2/3 is 0.666667, but then 2 * 1/3 will not equal 2/3. And 3 * 1/3 will not equal 1. But 1/3 + 2/3 will.
Just as 1/3 cannot be expressed exactly in decimal, 0.1 cannot be expressed exactly in binary. So testing for equivalence is not smart.

adding numbers in a for loop [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 6 years ago.
I tried to sum some numbers in a for loop but it didn't go as I expected
float sum = 0;
int i;
printf("0.1+0.1=%f\n", 0.1 + 0.1);
for (i = 0; i<1000000; i++)
{
sum = sum + 0.1;
}
printf("the sum need to be 100000 \n");
printf("the real sum is:\n %f\n", sum);
system("PAUSE");
this program prints:
0.1+0.1=0.200000
the sum need to be 100000
the real sum is:
100958.343750
Press any key to continue . . .
can you explain please this strange result?
the international standard for floating point numbers does not have an exact representation for some decimal numbers.
http://en.wikipedia.org/wiki/IEEE_754
It is due to the way they are stored in memory, the way the mantissa and exponent are stored.
https://en.wikipedia.org/wiki/Floating_point
This is also the reason why you should never compare two float numbers even if they look "the same".
I still remember how surprised I was the fist time a simple code comparing two float numbers didn't work :) This alone would open a dedicated universe of discussions. It is very worth reading anyway:
http://floating-point-gui.de/errors/comparison/
The floating numbers are stored in memory as x*2^y where x is between 0 and 1 with some precision and y is integer and so they accurately don't represent most numbers, they represent numbers "close enough".
When you do this addition multiple times, the error is just more visible.
You can use double type for better accuracy.

In C, why is the ratio 10.0/100 different from 0.1? [duplicate]

This question already has answers here:
Why Floating point numbers cant be compared? [duplicate]
(7 answers)
Closed 7 years ago.
This is a simple question and I searched the forums, but couldn't find an answer (I found one about Log but I don't think there is a rounding error here).
I wrote a program to determine the value of a fine for a range of expired products, but when the ratio is exact, the program will return the next fine category, ignoring the = sign in the conditional.
The program must return the following fines:
0 if no product is expired.
100 if up to 10% of products are expired.
10000 if more than 10% of products and up to 30% are expired.
100000 if more than 30% of products are expired.
This is the code I wrote:
#include <stdio.h>
int calculate_fine(int ncheckedproducts, int nexpiredproducts)
{
int fine;
float ratio;
ratio=(float)nexpiredproducts/ncheckedproducts;
if(nexpiredproducts==0)
fine=0;
else
if(ratio<=0.1)
fine=100;
else
if(ratio<=0.3)
fine=10000;
else
fine=100000;
return fine;
}
int main(void)
{
int ncheckedproducts, nexpiredproducts, fine;
printf("Type number of checked and expired products\n");
scanf("%d %d", &ncheckedproducts, &nexpiredproducts);
fine=calculate_fine(ncheckedproducts, nexpiredproducts);
printf("The fine is: %d\n", fine);
return 0;
}
But for values of 100 and 10, and 100 and 30, exactly 10% and 30% of expired products respectively, the program will return the wrong fine.
The teacher failed to explain me why, and corrected me to the following function:
int calculate_fine(int ncheckedproducts, int nexpiredproducts)
{
int fine;
if(nexpiredproducts==0)
fine=0;
else
if(nexpiredproducts<=0.1*ncheckedproducts)
fine=100;
else
if(nexpiredproducts<=0.3*ncheckedproducts)
fine=10000;
else
fine=100000;
return fine;
}
However, I wish to know why the first 10% ratio is greater than 0.1, and why I cannot use this approach.
This most probably is a rounding issue, but a different than you might think: Many finite decimal fractions do not have a finite binary fraction representation. Thus, some rounding the the closest number representable as a floating point number of the given type happens.
What you're dealing with is are the fine-grained aspects of floating point numbers:
Computers save floating point numbers in a binary format. Your float is probably a IEEE-754 single precision floating point number.
In those formats, you can typically represent only numbers exactly that are only a sum of a very limited amount of powers of two; for example, 0.75 is 2-1 + 2-2 and can hence be exactly reconstructed. In your case, you try to divide 100 = 26 + 25 + 22 by 3 = 21+20 and hope you get exactly the same result as 0.3 = 2-2+ 2-5+ 2-9+ 2-10+ 2-13+ ...
That won't happen.

Adding two doubles gives weird rounding result in C [duplicate]

This question already has answers here:
Why adding these two double does not give correct answer? [duplicate]
(2 answers)
Closed 8 years ago.
I'm a bit of C newbie but this problem is really confusing me.
I have a variable double = 436553940.0000000000 (it was cast from an Int) and an other variable double 0.095832496.
My result should be 436553940.0958324*96*, however I get 436553940.0958324*67*.
Why does this happen and how can I prevent it from happening?
The number you expect is simply not representable by a double. The value you receive is instead a close approximation based on rounding results:
In [9]: 436553940.095832496
Out[9]: 436553940.09583247
In [18]: 436553940.095832496+2e-8
Out[18]: 436553940.09583247
In [19]: 436553940.095832496+3e-8
Out[19]: 436553940.0958325
In [20]: 436553940.095832496-2e-8
Out[20]: 436553940.09583247
In [21]: 436553940.095832496-3e-8
Out[21]: 436553940.0958324
You've just run out of significand bits.
Doubles are not able to represent every number. We can write some C++ code (that implements doubles in the same way) to show this.
#include <cstdio>
#include <cmath>
int main() {
double x = 436553940;
double y = 0.095832496;
double sum = x + y;
printf("prev: %50.50lf\n", std::nextafter(sum, 0));
printf("sum: %50.50lf\n", sum);
printf("next: %50.50lf\n", std::nextafter(sum, 500000000));
}
This code computes the sum of the two numbers you are talking about, and stores it as sum. We then compute the next representable double before that number, and after that number.
Here's the output:
[11:43am][wlynch#watermelon /tmp] ./foo
prev: 436553940.09583240747451782226562500000000000000000000000000
sum: 436553940.09583246707916259765625000000000000000000000000000
next: 436553940.09583252668380737304687500000000000000000000000000
So, we are not able to have the calculation equal 436553940.0958324_96_, because that number is not a valid double. So the IEEE-754 standard (and your compiler) defines some rules that tell us how the number should be rounded, to reach the nearest representable double.

Resources