2

I am working through a beginning C++ class and my book(Starting Out with C++ Early Objects 7th edition) has a very poor example of how to check the value of a floating point variable.

The book example in question(filename pr4-04.cpp):

// This program demonstrates how to safely test a floating-point number
// to see if it is, for all practical purposes, equal to some value.
#include <iostream>
#include <cmath>
using namespace std;

int main()
{
    double result = .666667 * 6.0; 

    // 2/3 of 6 should be 4 and, if you print result, 4 is displayed.
    cout << "result = " << result << endl;

    // However, internally result is NOT precisely equal to 4.
    // So test to see if it is "close" to 4.
    if (abs(result - 4.0 < .0001))
        cout << "result DOES equal 4!" << endl;
    else
        cout << "result DOES NOT equal 4!" << endl;

return 0;
}

And I use g++ in Ubuntu to compile my code like this:

g++ pr4-04.cpp -o pr4-04 && ./pr4-04

And I get this error:

error: call of overloaded ‘abs(bool)’ is ambiguous

I am able to fix this by changing abs() to fabs(), but this is still super confusing! Why is the book giving us things which won't compile, or is this just me? Why does the cout of 'result' give 4 instead of 4.000002? Why does this value seem to change when it is used in the if{} statement?

I get that we can't just use == to check for equivalence, but why do I need to use the absolute value? I get the same answer whether or not I use it. So what is the point?

Not to mention, this seems like a very poor way to check for floating point equivalence. Is there a better way to do this? This topic seems awfully important.

I found this topic here on stackoverflow, but their solution:

fabs(f1 - f2) < precision-requirement
fabs(f1 - f2) < max(fabs(f1), fabs(f2)) * percentage-precision-requirement

Doesn't make much sense to me in the context of my 4 chapters worth of C++ experience. I would greatly appreciate some help. Our book has given me a whopping 6 sentences of text to explain all of this.

Edit: As suggested by some I tried to find an errata page, but after 30mins of searching the textbook, internet, and my course website I was only able to find this downloadable zip file, which required a login -_-

I also copied the code perfectly. That was not MY typo, I copied it directly from a CD with the code on it. It is also typed that way in the book.

Community
  • 1
  • 1
Robert
  • 123
  • 4
  • 3
    `abs(result - 4.0 < .0001)` should be `abs(result - 4.0) < .0001` – Mysticial Sep 29 '12 at 02:42
  • 1
    With regard to your question is there a better way to test for equality in floating point: The oft repeated advice to test with some interval is bad advice. The requirements of any application are specific to each application, so there is no general rule for what the interval should be or even whether it should be absolute or relative or something else. Floating-point errors may be tiny or huge, depending on operations used. Further, using an interval decreases the false rejection of equality but increases false acceptance (accepts as equal values that are unequal if computed exactly). – Eric Postpischil Sep 29 '12 at 12:34

2 Answers2

5
if (abs(result - 4.0 < .0001))

The parenthesis are wrong, you probably mean: if (abs(result-4.0) < .0001).

As to why it did not compile, the standard determines in §26.8p8 that

In addition to the double versions of the math functions in , C++ adds float and long double overloaded versions of these functions, with the same semantics.

The expression (result-4.0 < .0001) yields a bool, and there is no overload of abs that takes a bool argument, but there are multiple versions of abs for which the argument is implicitly convertible from bool. The compiler does not find one of the conversion sequences better than the rest and bails out with the ambiguity error.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Please pardon my newb-ness, but I think what you are saying is: The code abs(result - 4.0 < .0001) is expressing this |result - 4.0 < .0001| which is an impossible function. Instead of (abs(result-4.0) < .0001) which is expressing this |result - 4.0| < .0001 Is this correct? – Robert Sep 29 '12 at 03:34
  • @Robert: Right, except that `|result - 4.0 < .0001|` is not impossible in the C++ language. It becomes either `|true|` or `|false|`. There is no *absolute value of a bool*, but in C++ there are conversions from `bool` to other types (to `int`, and from `int` to other integral or floating point types), and the compiler is not able to pick one out of the rest. – David Rodríguez - dribeas Sep 29 '12 at 03:45
  • @dribeas: So the expression `(abs(result-4.0) < .0001)` works because the `abs(result-4.0)` is evaluated _before_ the if{} boolean values. The compiler cannot evaluate the true or false values of the equation if the expression is still +-|result - 4.0 < .0001| because the potential result could be either '.000002 <.001' or '-.000002 <-.001' and neither of these are a true false boolean value. – Robert Sep 29 '12 at 04:11
3

The problem is clearly the line

if (abs(result - 4.0 < .0001))

which should be written as

if (abs(result - 4.0) < .0001)

I would assume that this is a simple typo. Report the error to the author of the book!

BTW, the original code does compile on my system without any problem, giving the expected result! That is, even if the author tested the code he may not have noticed that it is problematic!

Also answering the question on why abs() is needed: some decimal numbers are rounded to a floating point value which is slightly smaller than the expected result while others are rounded to number which are slightly bigger. In which direction the values are round (if at all: some decimal numbers can be represented exactly using binary floating points) is somewhat hard to predict. Thus, the result may be slightly bigger or slightly smaller than the expectation and the difference, thus, positive or negative, respectively.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • It's probably in a list of errata somewhere (which the book should mention at the beginning) already. – chris Sep 29 '12 at 02:43