3

i have two double arrays, let's say A and B. i want to compare their results to 7 significant digits. will the following be correct to make the comparison?

k = pow(10,7);
for(...)
{
 if(((int)A[i]*k)!=((int)B[i]*k))
 {
  ...
 }
}
starter
  • 325
  • 1
  • 5
  • 15

4 Answers4

7

In order to compare doubles, you could use something like this:

bool fequal(double a, double b)
{
    return fabs(a-b) < epsilon;
}

Taken from here.

fabs reference.

But make sure you understand the potential pitfalls.

embedded.kyle
  • 10,976
  • 5
  • 37
  • 56
5

No, this will not work.

The type cast operator has higher precedence than the multiplication operator. This means that A[i] and B[i] will be cast to integers (and be truncated) before being multiplied by 1e7. 2.25 and 2.5 will end up being equal to your code. You can fix that by putting the multiplication in parentheses: (int)(A[i]*k)

Also, since you're relying on truncation instead of rounding, you may end up with incorrect results (depending on what you're expecting). 1.0e-7 and 1.9e-7 will be equal (1 == 1), while 1.9e-7 and 2.1e-7 will not (1 != 2). I suggest finding a function that will round properly with the behavior you desire.

Also, your comparison does not deal with significant digits, it simply changes the value of the exponent. In the above examples, there are only 2 significant digits, however your code would only compare one of those digits because the value of the exponent is -7.

Here is some code that does what you want:

//create integer value that contains 7 significant digits of input number
int adjust_num(double num) {
    double low_bound = 1e7;
    double high_bound = low_bound*10;
    double adjusted = num;
    int is_negative = (num < 0);
    if(num == 0) {
        return 0;
    }
    if(is_negative) {
        adjusted *= -1;
    }
    while(adjusted < low_bound) {
        adjusted *= 10;
    }
    while(adjusted >= high_bound) {
        adjusted /= 10;
    }
    if(is_negative) {
        adjusted *= -1;
    }
    //define int round(double) to be a function which rounds
    //correctly for your domain application.
    return round(adjusted);
}

...

if(adjust_num(A[i]) == adjust_num(B[i])) {
    ...
}
Eric Finn
  • 8,629
  • 3
  • 33
  • 42
  • Also, the values - especially after scaling - may be outside the `int` range, then casting to `int` yields implementation-defined results, often `INT_MIN`. – Daniel Fischer Jul 31 '12 at 19:30
  • @DanielFischer True, but that is only a problem because of how starter was adjusting the number to 7 significant figures. If he did it correctly, there would be no possibility of overflow on a 32-bit int. – Eric Finn Jul 31 '12 at 19:43
  • 1
    Yes, that was referring to the OP's method, not yours. One problem in your code is that it doesn't work for negative `double`s or 0 (also infinities and NaNs). A style issue: why `adjusted/low_bound < 1` and not `adjusted < low_bound`? – Daniel Fischer Jul 31 '12 at 19:49
  • AT_Eric_Finn: Thanks, the above seems to be working fine. But in case where i get -0.0000 or 0.00000 it says that they are not equal... – starter Jul 31 '12 at 20:04
  • @DanielFischer You're right about 0 and negative values. I've modified the code to account for that. I also agree with the style issue. Infinites and NaNs are something that make sense to handle separately from adjusting numbers to have a certain number of significant figures. – Eric Finn Jul 31 '12 at 20:05
  • @starter Interesting, the code I posted converts the numbers to integers, so there should be no negative 0. Are you comparing `double`s or `float`s that have these values, or does your platform have a negative 0 for integers? – Eric Finn Jul 31 '12 at 20:10
  • AT_Eric_Finn: i am comparing doubles. – starter Jul 31 '12 at 21:10
  • @starter Alright, doubles (and floats) can have a negative zero value which is different from positive zero. If you make your adjust_num function return an int, both 0.0 and -0.0 will be converted to 0, so comparing for equality will work. – Eric Finn Jul 31 '12 at 21:20
  • AT_Eric_Finn: it's strange but the cases where it says not equal are cases when both are 0.0000 or -0.0000. – starter Jul 31 '12 at 21:43
  • AT_Eric_Finn: adjust_num function returns an int. – starter Jul 31 '12 at 21:47
  • @starter Can you see what calling the adjust_num function returns for each input value? And you can try using a debugger to watch the adjusted value to figure out what's going on. – Eric Finn Jul 31 '12 at 21:55
  • @starter Excellent! Could you describe what was wrong (if anything) with the code I posted so that it can be corrected? – Eric Finn Aug 01 '12 at 13:10
1

Yes but you do have to make one change. try (int)(A[i]*k) to make sure that your multiplication get executed first.

Hope this helps.

Logan S.
  • 517
  • 1
  • 4
  • 15
0

When you are using two floating-point values to decide if the values they would ideally have are equal, you should have some estimate (or, better, a proven bound) of how far apart the calculated values could be if the exactly calculated values were equal. If you have such a bound, then you can perform a test like this: “If the two numbers are closer together than the error bound, then accept them as equal.” The error bound could be a single absolute number, or it could be a number relative to the magnitude of one of the values, or it could be some other function of the values.

However, there is another question you should answer. Sometimes, the above test will accept values as equal (because the two calculated values are close together, possibly even equal) even though the exactly calculated values would not be equal. So, you know whether accepting calculated values that are close to each other as equal even though the exactly calculated numbers are not equal will cause you problems. If the answer is yes, the above test will sometimes accept as equal numbers that will cause you problems, then you cannot use this test. You may have to perform your calculations a different way to reduce the errors.

Advice is often given to fabricate some seemingly small threshold and use it. This is sloppy programming and is not engineering.

As an aside, never write pow(10, 7). Write 1e7. This avoids any possibility of error in the function call and it may avoid an unnecessary functional call entirely.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312