Yesterday, a friend shared with me a problem using double. He was writing a Calculator app, and he run into this incorrect result:
123456789123456.78 * 1 = 123456789123456.8
Here is why.
NSExpression uses internally the C-based primitive double. Your example can be written as:
double a = 123456789123456.78;
double b = 1.0;
NSLog (@"Result %@ ", [NSNumber numberWithDouble: a*b]);
Why the result is 123456789123456.8 instead of 123456789123456.78? The primitive type double uses the IEEE-754 double precision format (
http://en.wikipedia.org/wiki/Double-precision_floating-point_format), also known as Bynary64 because uses 64 bits to store the number. Storing a decimal number in a binary format has a number of disadvantages. There is a inherent rounding error that depends of the magnitude of the number.
Between 2 ^52=4,503,599,627,370,496 and 2^53=9,007,199,254,740,992 the representable numbers are exactly the integers. So:
double a = 4503599627370496.6;
double b = 1.0;
NSLog (@"Result %@ ", [NSNumber numberWithDouble: a*b]);
Between 2^53 =9007199254740993to 2^54, everything is multiplied by 2, so the representable numbers are the even ones. So:
double a = 9007199254740993;
double b = 1.0;
NSLog (@"Result %@ ", [NSNumber numberWithDouble: a*b]);
Conversely, for the previous range from 2^51 to 2^52, the spacing is 0.5, etc.
In your case, the number 123456789123456 is in the interval 2^46 - 2^47 and the rounding error is 1 / 64 = 0.015625.
With 64 bits you can work only with a maximum of 15-17 decimal digits, and you'll get rounding errors in all the range.
The solution? You can use NSDecimalNumber that uses decimal arithmetic( Java has BigDecimal )
NSDecimalNumber *decimaNumber = [NSDecimalNumber decimalNumberWithString: @"4503599627370496.11"];
NSDecimalNumber *decimaNumber2 = [NSDecimalNumber decimalNumberWithString: @"1"];
NSLog(@" %@",[decimaNumber decimalNumberByMultiplyingBy:decimaNumber2]); //4503599627370496.11