Reputation: 241
I'm trying to get the user to input a number between 1.00000 to 0.00001 while edges not included into a float variable. I can assume that the user isn't typing more than 5 numbers after the dot. now, here is what I have written:
printf("Enter required Leibniz gap.(Between 0.00001 to 1.00000)\n");
scanf("%f", &gap);
while ((gap < 0.00002) || (gap > 0.99999))
{
printf("Enter required Leibniz gap.(Between 0.00001 to 1.00000)\n");
scanf("%f", &gap);
}
now, when I'm typing the smallest number possible: 0.00002 in getting stuck in the while loop. when I run the debugger I saw that 0.00002 is stored with this value in the float variable: 1.99999995e-005 anybody can clarify for me what am I doing wrong? why isn't 0.00002 meeting the conditions? what is this "1.99999995e-005" thing.
Upvotes: 1
Views: 1522
Reputation: 3558
float
s are not capable of storing exact values for every possible number (infinite numbers between 0-1 therefore impossible). Assigning 0.00002 to a float will have a different but really close number due to the implementation which is what you are experiencing. Precision decreases as the number grows.
So you can't directly compare two close floats and have healthy results.
More information on floating points can be found on this Wikipedia page.
What you could do is emulate fixed point math. Have an int n = 100000;
to represent 1.00000
internally (1000 -> 0.001 and such) and do calculations accordingly or use a fixed point math library.
Upvotes: 2
Reputation: 1503
Fraction part of single precision floating numbers can represent numbers from -2 to 2-2^-23 and have a fraction part with smallest quantization step of 2^-23. So if some value cannot be represented with a such step then it represented with a nearest value according to IEEE 754 rounding rules:
0.00002*32768 = 0.655360043 // floating point exponent is chosen.
0.655360043/(2^-23) = 5497558.5 // is not an integer multiplier
// of quantization step, so the
5497558*(2^-23) = 0.655359983 // nearest value is chosen
5497559*(2^-23) = 0.655360103 // from these two variants
First one variant equals to 1.999969797×10⁻⁵ in decimal format and the second one equals to 1.999999948×10⁻⁵ (just to compare - if we choose 5497560 we get 2.000000677×10⁻⁵). So the second variant can be choosen as a result and its value is not equal to 0.00002.
The total precision of floating point number depends on exponent value as well (takes values from -128 to 127): it can be computed by multiplication of fraction part quantization step and exponent value. In case of 0.00002 total precision is (2^-23)×(2^-15) = 3.6×(10^-12). It means if we add to 0.00002 a value which is smaller than a half of this value than 0.00002 remains the same. In general it means that numbers of floating point number which is meaningful are from 1×exponent to 2×(10^-23)×exponent.
That is why a very popular approach is to compare two floating numbers using some epsilon value which is greater than quantization step.
Upvotes: 2
Reputation: 241721
The problem here is that you are using a float
variable (gap
), but you are comparing it with a double
constant (0.00002
). The constant is double
because floating-point constants in C are double unless otherwise specified.
An underlying issue is that the number 0.00002
is not representable in either float
or double
. (It's not representable at all in binary floating point because it's binary expansion is infinitely long, like the decimal expansion of ⅓.) So when you write 0.00002
in a program, the C compiler substitutes it with a double
value which is very close to 0.00002
. Similarly, when scanf
reads the number 0.00002
into a float
variable, it substitutes a float
value which is very close to 0.00002
. Since double
numbers have more bits than floats
, the double
value is closer to 0.00002
than the float
value.
When you compare two floating point values with different precision, the compiler converts the value with less precision into exactly the same value with more precision. (The set of values representable as double
is a superset of the set of values representable as float
, so it is always possible to find a double
whose value is the same as the value of a float
.) And that's what happens when gap < 0.00002
is executed: gap
is converted to the double
of the same value, and that is compared with the double (close to) 0.00002
. Since both of these values are actually slightly less than 0.00002, and the double
is closer, the float
is less than the double
.
You can solve this problem in a couple of ways. First, you can avoid the conversion, either by making gap
a double
and changing the scanf
format to %lf
, or by comparing gap
to a float
:
while (gap < 0.00002F || gap > 0.99999F) {
But that's not really correct, for a couple of reasons. First, there is actually no guarantee that the floating point conversion done by the C compiler is the same as the conversion done by the standard library (scanf
), and the standard allows the compiler to use "either the nearest representable value, or the larger or smaller representable value immediately adjacent to the nearest representable value, chosen in an implementation-defined manner." (It doesn't specify in detail which value scanf
produces either, but recommends that it be the nearest representable value.) As it happens, gcc
and glibc
(the C compiler and standard library used on Linux) both produce the nearest representable value, but other implementations don't.
Anyway, according to your error message, you want the value to be between 0.00001
and 1.00000
. So your test should be precisely that:
while (gap <= 0.00001F || gap >= 1.0000F) { ...
(assuming you keep gap
as a float
.)
Any of the above solutions will work. Personally, I'd make gap
a double
in order to make the comparison more intuitive, and also change the comparison to compare against 0.00001
and 1.0000
.
By the way, the E-05
suffix means "times ten to the power of -5" (the E
stands for Exponent
). You'll see that a lot; it's a standard way of writing floating point constants.
Upvotes: 3
Reputation: 969
Like some of the comments said, due to how floating point numbers are represented, you will see errors like this. A solution to this is convert it to
gap + 1e-8 < 0.0002
This gives you a small window of tolerance enough to let most cases you want to pass and most you dont want to fail
Upvotes: 0