1

I have a program designed to test for the various instances of Side-Angle-Side triangles, but I'm getting a warning that I think might be screwing up my program.

Essentially, I have the two if-statements

if (sideA == sideB * sin(angA))

and

 else if (sideB > sideA && sideA > sideB * sin(angA))

Where sideA, sideB, and angA are all doubles and angA is entered as degrees and converted to radians via the equation angA = angA * 3.14159 / 180.

The issue I'm having is that my program is reading the latter statement when it should be reading the former. Specifically, when I enter sideA as 5, sideB as 10, and angA as 30, the expression sideB * sin(angA) is read as 5. For some reason though, instead of following through with this as "5 == 5 and therefore the first if statement should be ran," the contents of the "else if" statement is being ran.

The less likely reason for this issue in my mind is that I'm being too conservative with the number of digits of PI I'm using and I should increase it; the more likely reason is the warning I'm getting that states

warning: comparing floating point with == or != is unsafe [-Wfloat-equal]
     else if (sideA == sideB * sin(angA)) {
                                       ^

Which is something I have no clue how to deal with and cannot find out how to online.

Energya
  • 2,623
  • 2
  • 19
  • 24
Dan Boing
  • 31
  • 5
  • 3
    You should not compare floats that way. The compiler already warns you that what you wrote is not what you want. And indeed, you expected something different! Check if the absolute difference of the two floats is smaller than some epsilon. https://floating-point-gui.de/errors/comparison/ – pasbi Oct 08 '19 at 09:26
  • Remember that you shall avoid testing floating point value with strict equality. See https://stackoverflow.com/questions/588004/is-floating-point-math-broken –  Oct 08 '19 at 09:28
  • Check the `almost_equal` example @ [cppreference:](https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon) – Ted Lyngmo Oct 08 '19 at 09:32
  • Aside: rather than `3.14159`, consider `double my_pi = acos(-1);`. – chux - Reinstate Monica Oct 09 '19 at 13:15
  • To get a good answer, Dan Boing, you need to post a larger amount of code showing how `if (sideA == sideB * sin(angA))` is used. That line of code need replacement. – chux - Reinstate Monica Oct 09 '19 at 13:17

1 Answers1

1

When comparing floating point values for equality you always should check for the difference being less than a certain threshold. So instead of checking whether a==b you should check for abs(a-b) < THRESHOLD (with THRESHOLD being something like 0.001, for example)

This is due to the intrinsic inaccuracy of floating value and the fact that even if it looks exact, its binary representation might be inexact

julodnik
  • 359
  • 2
  • 8
  • 3
    "_This is a bad way to do it because a fixed epsilon chosen because it “looks small” could actually be way too large._" https://floating-point-gui.de/errors/comparison/ – Ted Lyngmo Oct 08 '19 at 09:28
  • @TedLyngmo: Good point! that is a more general approach which also covers the last 1%. – julodnik Oct 08 '19 at 09:31
  • @TedLyngmo I believe I actually have to do it this way as I am currently in an introductory C++ class; thus my teacher would most likely not take kindly to the code accounting for the "edge cases," and the "Comparing Float Point Numbers" paper mentioned at the bottom is likely beyond the scope of this course. – Dan Boing Oct 08 '19 at 09:42
  • 1
    Saying that, when comparing floating-point values for equality, “you always should check for the difference being less than a certain threshold” is bad advice. Accepting as equal numbers that differ by a small amount decreases false rejections of equality, but it increases false acceptances. Whether this is acceptable or not depends on the application—it is not correct general advice. [There is no general solution for comparing floating-point numbers that contain errors from previous operations.](https://stackoverflow.com/questions/21260776/how-to-compare-double-numbers/21261885#21261885). – Eric Postpischil Oct 08 '19 at 19:34
  • @julodnik Those `0.001` could cover 100% of the cases where a and b should _not_ be considered equal (that is, when dealing with numbers where epsilon is small). So not using a fixed threshold isn't just about covering corner cases - it's about not making too many assumptions. – Ted Lyngmo Oct 08 '19 at 22:25
  • @EricPostpischil Equality when it comes to floating-point values is always sensitive. If you have `a` and `b` but don't know what calculations that were made to get them, you'll have to go with _close enough_ - and then epsilon helps to set the threshold. To the best of my knowledge, using C++'s library support for this gets us as close as we can - in the general case. – Ted Lyngmo Oct 08 '19 at 22:47
  • 1
    @TedLyngmo: No, you do not have to go for close enough. One alternative is to recognize that floating-point is not the right tool for the problem and not use it. Of course, why would one use any numbers that they do not know the derivation of? If somebody handed you an `int` value and gave no information about how it was calculated, why would you assume it was correct? Floating-point is no different; if you do not have error/accuracy information, you should not use the numbers. Another alternative is to engineer computations that do what you want to do. – Eric Postpischil Oct 09 '19 at 01:46
  • @EricPostpischil In most applications I agree with your conclusion - but lets make a pocket calculator app. The user goes two different ways to calculate the result and gets `a` and `b` which are both correct. Although the storage bitpattern is slightly different, it's not obvious that the result shouldn't be considered equal. – Ted Lyngmo Oct 09 '19 at 01:55
  • @TedLyngmo: Pocket calculator applications generally do not have a “compare for equality” button. If they do, it ought to produce true if and only if its operands are equal. If two numbers a and b were both computed with the intent of calculating some real-number formula and a and b are not equal, then they are not both correct. At least one of them is wrong. It is wrong to think of floating-point arithmetic as computing correct real-number arithmetic. It is designed to approximate real-number arithmetic. – Eric Postpischil Oct 09 '19 at 02:12
  • @EricPostpischil That's the whole point in my book. The approximations needed for binary machines to deal with floating-points have limits. One could calculate `a` and `b` using two _proven_ ways to get the correct result - but a bit-pattern-only-comparator would say they are not equal. In such an application, it's best if the machine knows its limits. My programmable pocket calculator anno 1983 seemed to round off its own inaccuracy and compare such results equal - but I haven't put it to the test lately :-) – Ted Lyngmo Oct 09 '19 at 02:37
  • @TedLyngmo: It is not possible for a wrong answer to be proven correct, nor for a way of getting a wrong answer to be proven correct. – Eric Postpischil Oct 09 '19 at 10:43
  • @EricPostpischil Would you say that to get the sum of all elements in a range (the users input), starting from the first element and going forward, adding to the sum is correct? Would it be equally correct to start adding from the last element and go backwards (if the user happens to enter the data that way)? (_the commutative law_) Yet, doing this with floating point variables very often gives results that does not compare equal. – Ted Lyngmo Oct 09 '19 at 11:47
  • @TedLyngmo: No, adding all the numbers of a range in either order, or any order, is not guaranteed to give the real-number sum of the numbers in the range. – Eric Postpischil Oct 09 '19 at 13:14
  • @TedLyngmo: Also, the law you want in this case is the associative law, and floating-point arithmetic is not associative. – Eric Postpischil Oct 09 '19 at 13:15
  • @EricPostpischil Yes, the associative law, sorry. I'm starting to realize that when you said that it was wrong to say "_you always should check for the difference being less than a certain threshold_", your emphasis was on the "_always_" part, right? If so, I completely missed your point. My bad! – Ted Lyngmo Oct 09 '19 at 16:33