-1

i want to check if a float is equal to another but only X number after the dot. For X = 3: 0.1234 == 0.1235

if (float1.X == float2.X)
      do something
else
      do something
Grainbox
  • 31
  • 3
  • Are you trying to figure out whether `float1` and `float2` are the same or different, to a significance of three places past the decimal? If so, see [my answer](https://stackoverflow.com/questions/70754073/check-if-float-is-equal-to-another-x-number-after-dot-in-c/70757254#70757254). – Steve Summit Jan 18 '22 at 14:40
  • For the most part, you should not be attempting to compare floating-point numbers to a certain number of digits. There are only limited circumstances in which this is a correct solution to some problem. In general, it cannot compensate for rounding errors introduced by prior floating-point calculations. You should explain the context of what you are doing. – Eric Postpischil Jan 18 '22 at 23:27

2 Answers2

1

Floating point calculations are imprecise may lead to surprising rounding errors: Therefore I'll recommend that you avoid floating point calculations before comparing the fractional part. Instead use sprintf to print the values to strings. Then compare the number of decimals you want in the two stings. Make sure to use a sufficiently large width specifier to avoid unexpected roundings. For IEEE 754 32 bit floats the maximum number of decimals will be a little less than 150.

Start with:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    float f1 = 1.23456;
    float f2 = 1.23457;
    char str_f1[512];
    char str_f2[512];
    sprintf(str_f1, "%.160f", f1);
    printf("%s\n", str_f1);
    sprintf(str_f2, "%.160f", f2);
    printf("%s\n", str_f2);

    // Add code for string compare using X decimals
    
    return 0;
}

Output:

1.234560012817382812500000000000000000000000000000000000000000...
1.234570026397705078125000000000000000000000000000000000000000...

So str_f1 and str_f2 are now two simple strings so you it's easy to compare them with exactly the number of decimals that you want.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • `%.60f` is a fair shot at super-wide precession needs of a `float`. Note that the typical smallest `float` needs about 150 places for _exact_ decimal representation. IAC, `sprintf()` cannot reliable handle such, but an upper bound for what could be needed. I suspect this `sprint()` approach is likely the one a prof may be looking for, yet in the extremes cases (something I doubt OP needs), it is not sufficient. – chux - Reinstate Monica Jan 18 '22 at 11:34
  • 1
    Hmmm, DV here unwarranted as it is a workable approach given some limitations. Have an UV. – chux - Reinstate Monica Jan 18 '22 at 11:54
  • @chux-ReinstateMonica Thanks for your input. I'm kind of interested in cases where `sprintf` doesn't print the correct, exact value even if the width specifier is sufficient. – Support Ukraine Jan 18 '22 at 14:04
  • @4386427 I'm not precisely sure which case(s) chux might have been referring to, but it turns out that correctly converting a floating-point number to a decimal string representation is a surprisingly hard problem. Proper algorithms are known (cf. "dragon4", "grisu", and "ryu"), and good C libraries use them when implementing `%f` and `%e` for `printf` and `sprintf`, but poor libraries do not, and will display various anomalies. – Steve Summit Jan 18 '22 at 14:43
  • 4386427, @SteveSummit well said it. Further, C does not specify the quality of `sprintf()`, yet it is reasonable to assume the first N _significant_ decimal digits are correct. N >= `DBL_DECIMAL_DIG` (17) is needed for `double` which is what `"%f"` gets, to make round-tripping successful. IEEE-754 compliance requires, from my memory, N >= 20 (17 + 3). Yet for 2 `float`s, after `FLT_DECIMAL_DIG` (9) _significant_ digits, the distinctiveness is fully discernable, I revise my estimated precision needs down to 45 for `FLT_MIN` "'0.0000000000000000000000000000000000000117549435". So `"% .45f"` – chux - Reinstate Monica Jan 18 '22 at 16:06
  • 4386427, Recommend using `modff()` on `f1,f2` as that 1) makes the max buffer needs smaller and 2) makes comparing the strings simpler. – chux - Reinstate Monica Jan 18 '22 at 16:08
  • With respect to the question of "cases where `sprintf` doesn't print the correct, exact value", I'm reminded of [this question](https://stackoverflow.com/questions/70057191). (See especially Eric Postpischil's comment.) – Steve Summit Jan 18 '22 at 16:58
  • @chux-ReinstateMonica Ì think `%.45f` will too little. See https://ideone.com/WYiuCF – Support Ukraine Jan 18 '22 at 17:19
  • @4386427 But the goal is to compare 2 `float`s, not simply print the `float`. I still assert `printf("% .45f\n", FLT_TRUE_MIN);` is enough for the compare. Actually I'd go for `printf("% .*f\n", FLT_MANT_DIG - FLT_MIN_EXP, FLT_TRUE_MIN);` for portability. – chux - Reinstate Monica Jan 18 '22 at 17:23
  • @chux-ReinstateMonica The idea is to print to strings and then do the compare on strings. Printing only 45 decimals gives to few characters in the string: https://ideone.com/9ogmyc – Support Ukraine Jan 18 '22 at 17:39
  • @4386427 Sample code did not do any compare. Yet if it did, "0.00...0011754944" vs. "0.00...0000000001" would compare the same even with more digits. Same as with any 2 `float`s, even `FLT_TRUE_MIN` and `FLT_TRUE_MIN * 2`. – chux - Reinstate Monica Jan 18 '22 at 17:49
  • 1
    Re “Any calculation that you do on the two variables may change the fractional value”: No, a number of floating-point operations are known to be exact. These include multiplying and dividing by a power of the floating-point base, within bounds, taking a symmetric remainder (as in `remainder`), separating integer and fraction parts (as in `modf`), and subtracting two numbers of the same sign whose magnitudes differ by a factor of at most two. – Eric Postpischil Jan 18 '22 at 20:08
  • @chux-ReinstateMonica I'm not sure if the word "float" in the question title means: "Data type `float`" or: "Floating point number", which may correspond to the data type `double`... – Martin Rosenau Jan 19 '22 at 10:33
0

This isn't exactly what you asked, but if you're trying to determine whether float1 and float2 are approximately equal, to within X significant digits, the usual way to do it is to subtract the two numbers, and see if the difference is less than a threshold. (And, of course, you have to take the absolute value into account.) So, for three significant digits, something like this:

if(fabs(float1 - float2) < 0.001)
    do something;
else
    do something else;

Or, to do it with a variable X:

if(fabs(float1 - float2) < pow(10, -X))
    do something;
else
    do something else;
Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • This is likely close to what OP really needs. Yet unclear why use `double` functions and constants instead of `float` ones. – chux - Reinstate Monica Jan 18 '22 at 17:58
  • That won't work: If `X` is 1, `123 == 123.09` shall be true (although `123 - 123.09` is `0.09`) but `123 == 122.99999` shall be false (although `123 - 122.99999` = `0.0001`). – Martin Rosenau Jan 19 '22 at 09:58
  • @MartinRosenau Answer updated. This isn't what the OP explicitly asked for, but I suspect it might be what they actually need. (What the OP explicitly asked for has no practical value that I can see, while of course an 'approximately equal' comparison is a common need.) – Steve Summit Jan 19 '22 at 12:56