Reputation: 774
I'm new in unit testing and I decided to write a simple calculator to study the concepts, but I run into a problem that makes no sense to me. STAssertEquals is failing for 2 double values that are apparently the same. The error I'm getting is this one: '-2.89' should be equal to '-2.89':
The code that perform the calculation is:
- (double)calculateOperation:(int)operation numberA:(double)numA numberB:(double)numB
{
double result = 0;
switch(operation)
{
case addition:
result = numA + numB;
break;
case subtraction:
result = numA - numB;
break;
case multiplication:
result = numA * numB;
default:
break;
}
return result;
}
And the test is
- (void)testSubtraction
{
//Two double positive values
double test1 = [calculator calculateOperation:subtraction numberA:4.45 numberB:7.34];
STAssertEquals(test1, -2.89, nil);
}
I tried with different values and it seems that just a certain range of values causes the STAssertEquals to fail.
However if I do STAssertEquals((4.45 - 7.34), -2.89, nil) it works without any problem.
I really don't know what's wrong.
Upvotes: 0
Views: 137
Reputation: 29946
Consider the following setup:
double a = 4.45;
double b = 7.34;
double result = a - b;
double testVal = -2.89;
At this point, if you look at the actual values in memory of result
vs. testVal
you'll see the following:
(lldb) p/x *(uint64_t*)&testVal
(uint64_t) $1 = 0xc0071eb851eb851f
(lldb) p/x *(uint64_t*)&result
(uint64_t) $2 = 0xc0071eb851eb851e
So you can see that indeed there is a difference, likely due to rounding in the subtraction operation, in the very last bit of the significand of result
vs. the literal we put in testVal
. This difference may not be apparent when the formatting code converts the native binary representation to decimal, as these conversions are not always exact, but it still affects the literal equality of these two values.
Practically speaking, you probably want to use STAssertEqualsWithAccuracy
here, picking some value for the accuracy that is appropriate for your application. (For graphics work, I usually use 1e-6, but your mileage may vary.) Also, since everything is a double
, you don't need explicit casts here. A decimal numeric literal with no suffix is interpreted by the compiler as a double
(if you want a float
follow the decimal numeric literal with an F, and if you want a long double
follow it with an L. i.e. -2.89F or -2.89L)
It's worth reiterating: Although many folks like to think of floating point numbers in a decimal frame of reference (since that's how most of us learned arithmetic), that's not how they're stored, and not 'how they work'. You'll experience far less disappointment if you don't expect them to behave like base-10 numbers. This is kind of a classic example of that.
Upvotes: 2