Fmod function clearly outputs an expected double, yet if(fmod == expected double) doesn't evaluate to true

Viewed 74

When I call my function:

formatCurrency(7.5);

string formatCurrency(double cash) {
cout << "fmod(cash,.1) is equal to " << fmod(cash,.1) << endl;
if(fmod(cash,1) == 0) {
    cout << cash << ".00";
}
else if(fmod(cash,.1) == 0.1) {
    cout << cash << "0";
}
else if(fmod(cash,.01) == 0.01) {
    cout << cash;
}
else{
    cout << "Error: unable to display in currency format";
}
return "";
}

fmod(7.5,.1) is clearly equal to .1, and it even outputs as such when I run the program. But instead I get the following output:

fmod(cash,.1) is equal to 0.1
Error: unable to display in currency format

What gives? I see literally nothing wrong with my code. The code does work with whole numbers/the first if statement, but anything with a decimal makes things get dicey.

1 Answers

Floating point data types are finite and suffer from imprecision. And the way they are encoded makes it very unintuitive for beginners as to for which values they are imprecise (e.g. 0.5 will be represented precisely, 0.1 needs to be rounded).

There are entire books written about the ins and outs of the IEEE floating point encodings, but for a beginner any Wikipedia article is a good start to get a feel of it: https://en.wikipedia.org/wiki/Floating-point_arithmetic

There are different solution for this problem, depending on your domain, e.g. using arbitrary precision libraries like MPFR (https://www.mpfr.org/). Most unit test frameworks settle for not using == equality, but instead introduce a "close enough" comparison. As an example, JUnit's assertEquals for double effectively checks the following:

Math.abs(d1 - d2) <= some_small_delta

see also: JUnit assertEquals(double expected, double actual, double epsilon)

So if the difference between the two numbers is small enough, they are considered equal. In the context of currency, one common alternative is to decide on the smallest denomination that your system needs (e.g. perhaps "cents" when the currency is "dollar") and always represent "1 dollar" as an int with value 100 instead of a floating point with value 1.0. There are even libraries to simplify this task, effectively modelling fixed point data types.

see also:

Related