Difference in floating point comparison C - c

float a = 0.7;
if(a<0.7)
printf("true");
else
printf("false");
OUTPUT : true
Now , if I change the value of a to say 1.7 ,then
float a = 1.7;
if(a<1.7)
printf("true");
else
printf("false");
OUTPUT : false
Since 0.7 is treated as a double (HIGH PRECISION) and a is a float (LESS PRECISION) , therefore a < 0.7 , and in second case it should be the same again , so it should also print true. Why the difference in output here ?
PS : I have already seen this link.

Since you saw my answer to the question you linked, let's work through it and make the necessary changes to examine your second scenario:
In binary, 1.7 is:
b1.1011001100110011001100110011001100110011001100110011001100110...
However, 1.7 is a double-precision literal, whose value is 1.7 rounded to the closest representable double-precision value, which is:
b1.1011001100110011001100110011001100110011001100110011
In decimal, that's exactly:
1.6999999999999999555910790149937383830547332763671875
When you write float a = 1.7, that double value is rounded again to single-precision, and a gets the binary value:
b1.10110011001100110011010
which is exactly
1.7000000476837158
in decimal (note that it rounded up!)
When you do the comparison (a < 1.7), you are comparing this single-precision value (converted to double, which does not round, because all single-precision values are representable in double precision) to the original double-precision value. Because
1.7000000476837158 > 1.6999999999999999555910790149937383830547332763671875
the comparison correctly returns false, and your program prints "false".
OK, so why are the results different with 0.7 and 1.7? It's all in the rounding. Single-precision numbers have 24 bits. When we write down 0.7 in binary, it looks like this:
b.101100110011001100110011 00110011...
(there is space after the 24th bit to show where it is). Because the next digit after the 24th bit is a zero, when we round to 24 bits, we round down.
Now look at 1.7:
b1.10110011001100110011001 10011001...
because we have the leading 1., the position of the 24th bit shifts, and now the next digit after the 24th bit is a one, and we round up instead.

If float and double are IEEE-754 32 bit and 64 bit floating point formats respectively, then the closest float to exactly 1.7 is ~1.7000000477, and the closest double is ~1.6999999999999999556. In this case the closest float just happens to be numerically greater than the closest double.

0.7 and 1.7 aren't represented the same way in base-2 - so the one might be slightly more and the other slightly fewer than the actual (exact) value.

It all has to do with base 2 representation of floating-point. Here is a good reference about the subject: What Every Computer Scientist Should Know About Floating-Point Arithmetic.

Related

Comparison of one float variable to its contained value

#include<stdio.h>
int main()
{
float a,b;
a=4.375;
b=4.385;
if(a==4.375)
printf("YES\n");
else
printf("NO\n");
if(b==4.385)
printf("YES\n");
else
printf("NO\n");
return 0;
}
Answer of this code :
YES
NO
I always thought if i compare a float with double value. it will never match to it. unless value is pure integer. but here float "a" has 4.375 is exact in it but "b" doesn't
printf("%0.20f\n",a);
printf("%0.20f\n",b);
This prints :
4.37500000000000000000
4.38500022888183593750
but if i print
printf("%0.20f\n",4.475);
It prints 4.47499990463256835938
How is this rounding effect is showing in some and not in others.
Can anyone explain this. how should "WE" judge when value in float variable will match to that contained in it and when it doesn't ?
The conversion from decimal fraction to a binary fraction is precise only if the decimal fraction can be summed up by binary fractions like 0.5, 0.25, ..., etc.
For example in your case
0.375 = 0.25 + 0.125 = 2-2 + 2-3
So it can be represented exactly by using binary fractions.
Where as the number 0.385 can not be represented by using binary fractions precisely. So numbers like 0.5, 0.25, 0.125, ..., etc. or a combination of these numbers can be represented exactly as floating point numbers. Others like 0.385 will give incorrect results when the comparison or equality operations are performed on them.
Floating points aren't magic.
They contain an exact value and if you compare it with that they will compare equal. The two problems are
1) Some operations are not always entirely exact due to precision issues. If you add one to a float and then subtract one then adding that one might have causes some loss of precision in the least significant value bits and when you subtract it you don't get back to quite the same value you expect.
2) It is not possible to exactly represent every decimal value in the floating point binary format. For example it is not possible to store the exact value of 0.1 in a floating point binary number in exactly the same way that you can't write the value of 1/3.0 as a decimal value exactly no matter how many digits you use.
But in your case if you store a value and compare it with that same value they SHOULD compare equal as they'll both have the same issues in the same way.
Your issue though is that you are not comparing like with like.
4.375 and 4.385 are not floats they are doubles and get converted to be stored so when you compare them later it's possible that the converted value is not quite identical. If you write 4.385f and 4.385f to use float values you should get YES both times.

Float value confusion in C

My code is
void main()
{
float a = 0.7;
if (a < 0.7)
printf("c");
else
printf("c++");
}
It prints C and this is fine as a treated as double constant value and its value will be 0.699999 which is less than 0.7.
Now if I change the value to 0.1,0.2,0.3 till 0.9 in a and also at if condition then it prints C++, apart from 0.7 and 0.9 that mean both are equal or a is greater.
Why this concept not consider for all value?
What "concept" are you talking about?
None of the numbers you mentioned are representable precisely in binary floating-point format (regardless of precision). All of the numbers you mentioned end up having infinite number of binary digits after the dot.
Since neither float nor double have infinite precision, in float and double formats the implementation will represent these values approximately, most likely by a nearest representable binary floating-point value. These approximate values will be different for float and double. And the approximate float value might end up being greater or smaller than the approximate double value. Hence the result you observe.
For example in my implementation, the value of 0.7 is represented as
+6.9999998807907104e-0001 - float
+6.9999999999999995e-0001 - double
Meanwhile the value of 0.1 is represented as
+1.0000000149011611e-0001 - float
+1.0000000000000000e-0001 - double
As you can see, double representation is greater than float representation in the first example, while in the second example it is the other way around. (The above are decimal notations, which are rounded by themselves, but they do have enough precision to illustrate the effect well enough.)

Casting to int and floating point errors?

myInt = int( 5 * myRandom() )
myRandom() is a randomly generated float, which should be 0.2.
So this statement should evaluate to be 1.
My question: is it possible that due to a floating point error it will NOT evaluate to 1?
For example if due to a floating point error something which should be 0.2 could that be LESS than that?
IE, for instance consider the following 3 possibilities:
int(5 * 0.2 ) = 1 //case 1 normal
int(5 * 0.2000000000000001 ) = 1 //case 2 slightly larger, its OK
int(5 * 0.1999999999999999 ) = 0 //case 3 negative, is NOT OK, as int() floors it
Is case3 even possible?, with 0.1999999999999999 be a result of a floating point error? I have never actually seen a negative epsilon so far, only case 2, when its a slightly bit larger, and thats OK, as when it is cast to int(), that 'floors' it to the correct result. However with a negative epsilon the 'flooring' effect will make the resulting 0.9999999999999996 evaluate to 0.
It is impossible for myRandom to return .2 because .2 is not representable as a float or a double, assuming your target system is using the IEEE 754 binary floating-point standard, which is overwhelmingly the default.
If myRandom() returns the representable number nearest .2, then myInt will be 1, because the number nearest .2 representable as a float is slightly greater than .2 (it is 0.20000000298023223876953125), and so is the nearest representable double (0.20000000000000001110223024625156540423631668090820312).
In other cases, this will not be true. E.g., the nearest double to .6 is 0.59999999999999997779553950749686919152736663818359375, so myInt will be 2, not 3.
Yes, it's possible, at least as far as the C standard is concerned.
The value 0.2 cannot be represented exactly in a binary floating-point format. The value returned by myRandom() will therefore be either slightly below, or slightly above, the mathematical value 0.2. The C standard permits either result.
Now it may well be that IEEE semantics only permit the result to be slightly greater than 0.2 -- but the C standard doesn't require IEEE semantics. And that's assuming that the result is derived as exactly as possible from the value 0.2. If the value is generated from a series of floating-point operations, each of which can introduce a small error, it could easily be either less than or greater than 0.2.
It's not a floating point error, it's the way floating point works. Any fraction that isn't 1/(power of 2) can't be exactly represented, and will be rounded either up or down to the nearest representable number.
You can fix your code by multiplying by some small epsilon greater than one before converting to integer.
myInt = int( 5 * myRandom() * 1.000000000000001 )
See What Every Computer Scientist Should Know About Floating-Point Arithmetic.
It's possible, depending on the number you choose.
To check a specific number you can always print them with a lot of precision: printf("%1.50f", 0.2)
why not multiply your float by 5.0 and then use the round function to properly round it?

Can anyone explain me this feature simply?

I have the following code,
float a = 0.7;
if(0.7 > a)
printf("Hi\n");
else
printf("Hello\n"); //Line1
and
float a = 0.98;
if(0.98 > a)
printf("Hi\n");
else
printf("Hello\n"); //Line2
here line1 outputs Hi but Line2 outputs Hello. I assume there would be a certain criteria about double constant and float, i.e any one of them would become larger on evaluation. But this two codes clarify me that situation can be come when double constant get larger and some other times float get larger. Is there any rounding off issue behind this? If it is, please explain me. I am badly in need of this clear..
thanks advance
What you have is called representation error.
To see what is going on you might find it easier to first consider the decimal representations of 1/3, 1/2 and 2/3 stored with different precision (3 decimal places or 6 decimal places):
a = 0.333
b = 0.333333
a < b
a = 0.500
b = 0.500000
a == b
a = 0.667
b = 0.666667
a > b
Increasing the precision can make the number slightly larger, slightly smaller, or have the same value.
The same logic applies to binary floating point numbers.
float a = 0.7;
Now a is the closest single-precision floating point value to 0.7. For the comparison 0.7 > a that is promoted to double, since the type of the constant 0.7 is double, and its value is the closest double-precision floating point value to 0.7. These two values are different, since 0.7 isn't exactly representable, so one value is larger than the other.
The same applies to 0.98. Sometimes, the closest single-precision value is larger than the decimal fraction and the closest double-precision number smaller, sometimes the other way round.
This is part of What Every Computer Scientist Should Know About Floating-Point Arithmetic.
This is simply one of the issues with floating point precision.
While there are an infinite number of floating point numbers, there are not an infinite number of floating point representations due to the bit-constraints. So there will be rounding errors when using floats in this manner.
There is no criteria for where it decides to round up or down, that would probably be language -implementation or compiler dependent.
See here: http://en.wikipedia.org/wiki/Floating_point, and http://en.wikipedia.org/wiki/IEEE_754 for more details.

exact representation of floating points in c

void main()
{
float a = 0.7;
if (a < 0.7)
printf("c");
else
printf("c++");
}
In the above question for 0.7, "c" will be printed, but for 0.8, "c++" wil be printed. Why?
And how is any float represented in binary form?
At some places, it is mentioned that internally 0.7 will be stored as 0.699997, but 0.8 as 0.8000011. Why so?
basically with float you get 32 bits that encode
VALUE = SIGN * MANTISSA * 2 ^ (128 - EXPONENT)
32-bits = 1-bit 23-bits 8-bits
and that is stored as
MSB LSB
[SIGN][EXPONENT][MANTISSA]
since you only get 23 bits, that's the amount of "precision" you can store. If you are trying to represent a fraction that is irrational (or repeating) in base 2, the sequence of bits will be "rounded off" at the 23rd bit.
0.7 base 10 is 7 / 10 which in binary is 0b111 / 0b1010 you get:
0.1011001100110011001100110011001100110011001100110011... etc
Since this repeats, in fixed precision there is no way to exactly represent it. The
same goes for 0.8 which in binary is:
0.1100110011001100110011001100110011001100110011001101... etc
To see what the fixed precision value of these numbers is you have to "cut them off" at the number of bits you and do the math. The only trick is you the leading 1 is implied and not stored so you technically get an extra bit of precision. Because of rounding, the last bit will be a 1 or a 0 depending on the value of the truncated bit.
So the value of 0.7 is effectively 11,744,051 / 2^24 (no rounding effect) = 0.699999988 and the value of 0.8 is effectively 13,421,773 / 2^24 (rounded up) = 0.800000012.
That's all there is to it :)
A good reference for this is What Every Computer Scientist Should Know About Floating-Point Arithmetic. You can use higher precision types (e.g. double) or a Binary Coded Decimal (BCD) library to achieve better floating point precision if you need it.
The internal representation is IEE754.
You can also use this calculator to convert decimal to float, I hope this helps to understand the format.
floats will be stored as described in IEEE 754: 1 bit for sign, 8 for a biased exponent, and the rest storing the fractional part.
Think of numbers representable as floats as points on the number line, some distance apart; frequently, decimal fractions will fall in between these points, and the nearest representation will be used; this leads to the counterintuitive results you describe.
"What every computer scientist should know about floating point arithmetic" should answer all your questions in detail.
If you want to know how float/double is presented in C(and almost all languages), please refert to Standard for Floating-Point Arithmetic (IEEE 754) http://en.wikipedia.org/wiki/IEEE_754-2008
Using single-precision floats as an example, here is the bit layout:
seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm meaning
31 0 bit #
s = sign bit, e = exponent, m = mantissa
Another good resource to see how floating point numbers are stored as binary in computers is Wikipedia's page on IEEE-754.
Floating point numbers in C/C++ are represented in IEEE-754 standard format. There are many articles on the internet, that describe in much better detail than I can here, how exactly a floating point is represented in binary. A simple search for IEEE-754 should illuminate the mystery.
0.7 is a numeric literal; it's value is the mathematical real number 0.7, rounded to the nearest double value.
After initialising float a = 0.7, the value of a is 0.7 rounded to float, that is the real number 0.7, rounded to the nearest double value, rounded to the nearest float value. Except by a huge coincidence, you wouldn't expect a to be equal to 0.7.
"if (a < 0.7)" compares 0.7 rounded to double then to float with the number 0.7 rounded to double. It seems that in the case of 0.7, the rounding produced a smaller number. And in the same experiment with 0.8, rounding 0.8 to float will produce a larger number than 0.8.
Floating point comparisons are not reliable, whatever you do. You should use threshold tolerant comparison/ epsilon comparison of floating points.
Try IEEE-754 Floating-Point Conversion and see what you get. :)

Resources