-3

Is there any way to ensure that a floating-point variable entered by the user, having 2 decimal places after the decimal point, retains its exact value, rather than losing precision?

This is the example case: I want to round a float with 50 numbers after radix point, like this

Before rounding = 0.70999997854232788085937500000000000000000000000000

to this:

After rounding = 0.71000000000000000000000000000000000000000000000000

I became confused when I wanted to compare a float number in a condition like this:

== Program Start==
Input : 0.71

/* program logic */
if (input == 0.71) {
    printf("True !");     
} else {
    printf("False !");
}

Output : False !
==Program End==

The output was False ! and will always be False ! because the true value of user's input is 0.70999997854232788085937500000000000000000000000000, and not 0.71000000000000000000000000000000000000000000000000

Is there any way to round a float value like that? I read about the potential for inaccuracies with floating point here:

However, these don't answer my question. If I use ceilf(input * 100) / 100 function, it will make the input 0.71 into 0.71000 when printf()d using %.5f format - which seems to work. But when I print with %.50f, the real input value appears as 0.709999978542327880859375. So, I can't compare to that value in my condition.

So, if floating point can never be accurate, what is the trick for the program logic above to get a true return value at that condition?

Community
  • 1
  • 1
  • You can safely remove all of these trailing zeros from the question. they don't give anything except of cluttering the question. – Eugene Sh. Jul 25 '16 at 19:13
  • 1
    Read about [Comparing Floating Point Numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/). – Some programmer dude Jul 25 '16 at 19:13
  • A `float` value cannot even hold 50 decimal places: `double` can only hold about 16. Please [read this](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). – Weather Vane Jul 25 '16 at 19:14
  • 1
    [Rounding Number to 2 Decimal Places in C](http://stackoverflow.com/questions/1343890/rounding-number-to-2-decimal-places-in-c) - you mentioned this and other guaranteed duplicates, but omitted to explain why you are ignoring them, i.e. why you _think_ they don't solve this. – underscore_d Jul 25 '16 at 19:21
  • Anyway, floating point **is** always _accurate_ to specific extents; it just can't always be _precise_ for all numbers. These are well-defined concepts. – underscore_d Jul 25 '16 at 19:29
  • thank u @underscore_d for helping me edit my question, i was edit again my question about duplicate question as u told, please read :) , i'm sorry if i'm wrong with my statement about that duplicate question. I'm newbie in C :) – William Luisan Jul 27 '16 at 17:42
  • i need to read @JoachimPileborg suggested link, i interets with it, before i close this question or mark for one true answer here after i test it all. – William Luisan Jul 27 '16 at 17:49
  • @WilliamLuisan The point is that rather than worrying about tiny imprecisions in the _stored_ value, you just need to **compare** it in a proper, reliable way - which has been explained many times already, _ad nauseam_, in any half-decent discussion of floating-point representation. This is not a new question, by any means, and it _is_ answered by (many) duplicates. Also, you should probably use **`double`** variables, as they'll retain additional precision (albeit won't remove your obligation to use proper methods of comparison). – underscore_d Jul 28 '16 at 09:56

3 Answers3

4

All of the user's input comes in as text. The easiest -- and, possibly, safest -- way to check it is to compare it as a string before even converting to number:

if (strcmp("0.71", input) == 0) {
    printf("True !");
} else {
    printf("False !");
}

Of course, this may not be acceptable to you, if you wish to check for something other than equality :)

The other point is that you are interested in fixed, rather than floating numbers here -- but are confusing them with actual integers. If hundredths is what you'd like to work with, then work with the integer numbers of hundredths... For example, consider money -- instead of using float (or double) to store the amounts of dollars (like $7.61), use int (or, god bless you, long) to store cents (like ¢761).

Similarly, for another example, if you are counting time and need precision of one thousandth of a second, then use integer numbers of milliseconds, instead of floating-point number of seconds.

This will sidestep your entire problem altogether and may also make your programs faster...

Mikhail T.
  • 3,043
  • 3
  • 29
  • 46
  • Do you understand that such an approach is adding a *huge* performance overhead? – Eugene Sh. Jul 25 '16 at 19:23
  • Actually, it is quite the opposite... Integers are usually faster than floats. – Mikhail T. Jul 25 '16 at 19:24
  • I mean the `strcmp` approach – Eugene Sh. Jul 25 '16 at 19:25
  • He is talking about user-input, which came in as text. `strcmp` is much faster, than converting the text into a number was -- and then you still need to compare the result of the conversion with the number of your own. – Mikhail T. Jul 25 '16 at 19:26
  • i interest with this answer, i was looking about this, comparing float with another trick :D , but i have to test it, before i mark that your answer as the true answer :D (y) – William Luisan Jul 27 '16 at 17:47
  • @underscore_d, I'm hopeful, William was referring to the other part of my proposal -- where I suggested, he use _fixed_ variables to store and manipulate _fixed_-precision values. Even if the values aren't integer... – Mikhail T. Jul 28 '16 at 14:02
  • @MikhailT. Yeah, sorry, I was commenting based on having half-read it :/ deleted. Yours is a good idea overall, for units that lend themselves to discrete steps and so long as it's followed all the way. – underscore_d Jul 28 '16 at 14:07
3

You can test equality with a range as you've experienced. Just rewrite your equality test with:

if(input <= .71 + EPSILON && input >= .71 - EPSILON) {
}

Here you can create your own EPSILON based on your tolerance, or you can perhaps use FLT_EPSILON from #include<cfloat> for C++ or #include <float.h> in C.

Recall that floats do not have 50 digits of decimal precision. So trying to round at the 50th decimal place isn't going to work out so well. Floats have about 6 (sometimes more) digits of precision.

Shaun Ramsey
  • 562
  • 4
  • 14
  • of course, in reality, one would factor such a test into a reusable function taking the compared value (and possibly a call-time epsilon, etc.) – underscore_d Jul 26 '16 at 08:49
  • 1
    Sure, that could be done. Here's a code snippet that should work: `int equal_floats(float first, float second, float EPSILON) { return first <= second+EPSILON && first >= second-EPSILON;}` and then, this call would be `if(equal_floats(input,.71,0.0001))` or something to that affect. Depending on your version, you might consider changing the int to bool as well in the function prototype. – Shaun Ramsey Jul 28 '16 at 01:42
  • That would be `#include ` as this is a **C** question. – Nisse Engström Aug 08 '16 at 05:29
3

I'm sure this question has been answered before, however:

float rounded_down = floorf(input * 100) / 100;   /* Result: 0.70 */
float nearest = roundf(input * 100) / 100;  /* Result: 0.71 */
float rounded_up = ceilf(input * 100) / 100; /* Result: 0.71 */

The library this functions belongs I think is math.h.

  • I'm sorry for my English too, I hope you understand. Anyhow the code I posted must do it for any case. – Héctor J. Vásquez Jul 25 '16 at 19:18
  • i was edit my question, please read again, i'm sorry if i'm wrong to prove that your answer from that Q&A thread doesn't fullfill my Question :) – William Luisan Jul 27 '16 at 17:31
  • This, on its own, does not provide an answer to the question since you haven't explained what you're supposed to do with it. Also, `. 70` and `. 71` cannot be represented exactly in binary floating point, so `rounded_down` will actually be something like `. 699999988079071044921875`, and so on. – Nisse Engström Aug 08 '16 at 06:44