1

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.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
user3921
  • 241
  • 2
  • 3
  • 12
  • What's the type of `gap`? – Kerrek SB Nov 09 '13 at 00:57
  • 5
    your conditions don't match your user instructions! 0.00001 < 0.00002 – Mitch Wheat Nov 09 '13 at 00:57
  • 5
    The range in the prompt is inconsistent with the numbers you're checking against. `0.00002` is not the immediate successor of `0.00001`; there are plenty of values between them. Once you fix that, you'll find that the value `0.00001` cannot be represented exactly in binary floating-point. – Keith Thompson Nov 09 '13 at 00:59
  • I am allowed to assume that the user won't type more than 5 numbers after the dot. – user3921 Nov 09 '13 at 01:00
  • 2
    Why not `gap <= 0.00001 || gap >= 1.0`? – huon Nov 09 '13 at 01:02
  • 1
    `1.99999995e-005` means `1.99999995 * 10^(-5)`. This is how floating point numbers are represented in computer: exponent (number from 1 to 2) and mantissa (power of 10). Because of that, you can not represent certain numbers accurately in binary format, so you have some data loss, and in your case the "rounded" number does not fit your conditions – medvedNick Nov 09 '13 at 01:02
  • possible duplicate of [Floating point numbers do not work as expected](http://stackoverflow.com/questions/7350558/floating-point-numbers-do-not-work-as-expected) – Dennis Meng Nov 09 '13 at 01:06

4 Answers4

3

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 &frac13;.) 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.

rici
  • 234,347
  • 28
  • 237
  • 341
2

floats 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.

Etherealone
  • 3,488
  • 2
  • 37
  • 56
  • 3
    `floats` are exact values. It the operation that is inexact. Going from a string like "0.00002" to a `float` often ends up in an inexact _conversion_, not an inexact `float` result. – chux - Reinstate Monica Nov 09 '13 at 05:51
2

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.

Michael
  • 1,505
  • 14
  • 26
0

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

Jamil Seaidoun
  • 949
  • 1
  • 11
  • 24