简体   繁体   中英

How to check for floating point precision in Double

Take this simple function for example.

   int checkIfTriangleIsValid(double a, double b, double c) {
      //fix the precision problem
      int c1, c2, c3;
      c1 = a+b>c ? 0 : 1;
      c2 = b+c>a ? 0 : 1;
      c3 = c+a>b ? 0 : 1;
      if(c1 == 0 && c2 == 0 && c3 == 0)
        return 0;
      else {
        printf("%d, %d, %d\n",c1, c2, c3);
        return 1;
      }
   } 

I place for a = 1.923 , b = 59.240 , c = 61.163

Now for some reason when I check for the condition in c1 it should give me 1, but instead, it gives me 0. I tried to do a printf with %.30f and found that the values later changes.

How can I fix this problem?

EDIT: I checked the other questions that are similar to mine but they don't even have a double.

Likely your C implementation uses the IEEE-754 basic 64-bit binary floating-point format for double . When 1.923 , 59.240 , and 61.163 are properly converted to the nearest values representable in double , the results are exactly:

  • 1.9230000000000000426325641456060111522674560546875,
  • 59.24000000000000198951966012828052043914794921875, and
  • 61.1629999999999967030817060731351375579833984375.

As you can see, the first two of these sum to more than the third. This means that, by the time you assign these values to double objects, they have already been altered in a way that changes their relationship. No subsequent calculations can repair this, because the original information is gone.

Since no solution after conversion to double can work, you need a solution that operates before or instead of conversion to double . If you want to compute exactly, or more precisely, with the values 1.923, 59.240, and 61.163, you may need to write your own decimal arithmetic code or find some other code that supports decimal arithmetic. If you only want to work with numbers with three decimal places, then a possible solution is to write some code that reads input such as “59.240” and returns it in an integer object scaled by 1000, so that 59240 is returned. The resulting values could then easily be tested for the triangle inequality.

when I check for the condition in c1 it should give me 1, but instead it gives me 0
How can I fix this problem?

Change your expectations.

A typical double can represent exactly about 2 64 different values. 1.923, 59.240, 61.163 are typically not in that set as double is usually encoded in a binary way. eg binary64 .

When a,b,c are assigned 1.923, 59.240, 61.163, they get values more like the below which are the closet double .

a      1.923000000000000042632564145606...
b     59.240000000000001989519660128281...
c     61.162999999999996703081706073135...

In my case, the a , and b both received a slightly higher value than the decimal code form, while c received a slightly lower one.

When adding a+b , the sum was rounded up, further away from c .

printf("a+b %35.30f\n", a+b);
a+b   61.163000000000003808509063674137

a + b > c was true, as well as other compares and OP's
checkIfTriangleIsValid(1.923, 59.240, 61.163) should return valid (0) as it is really more like checkIfTriangleIsValid(1.9230000000000000426..., 59.24000000000000198..., 61.16299999999999670...)


Adding a+b is further complicated in that the addition may occur using double or long double math. Research FLT_EVAL_METHOD for details. Rounding mode also can affect the final sum.

#include <float.h>
printf("FLT_EVAL_METHOD %d\n", FLT_EVAL_METHOD);

As to an alternative triangle check, subtract the largest 2 values and then compare against the smallest.

a > (cb) can preserve significantly more precision than (a+b) > c .

// Assume a,b,c >= 0
int checkIfTriangleIsValid_2(double a, double b, double c) {
  // Sort so `c` is largest, then b, a.
  if (c < b) {
    double t = b; b = c; c = t;
  }
  if (c < a) {
    double t = a; a = c; c = t;
  }
  if (a > b) {
    double t = b; b = a; a = t;
  }
  // So far, no loss of precision is expected due to compares/swaps.
  // Only now need to check a + b >= c for valid triangle

  // To preserve precision, subtract from `c` the value closest to it (`b`).
  return a > (c-b);
}

I will review more later as time permits. This approach significant helps for a precise answer - yet need to assess more edge cases. It reports a valid triangle checkIfTriangleIsValid_2(1.923, 59.240, 61.163)) .

FLT_EVAL_METHOD , rounding mode and double encoding can result in different answers on other platforms.


Notes:
It appears a checkIfTriangleIsValid() returning 0 means valid triangle .
It also appears when the triangle has 0 area, the expected result is 1 or invalid .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM