views:

63

answers:

2

Hi,

It started quite normally. I'm writing complex calculation application for iPhone. I decided to use float primitive for representing numbers. So, time came to start rounding and formatting output, but first of all rounding. I want to round numbers to different number of decimal places. FOund out there is no useful C function. OK, so I can write my own. But then I have numbers from application settings to persist. I found out only objects can be persisted. So I started to dig in NSNumber and NSDecimalNumber. Especially the latter gives me shivers only when look at it. Delegate needed to implement roundings, no smiple arithmetic operators, special methods instead....:( What is left then of pure C simplicity?

Is there any other way to simplify this?

+1  A: 

Use NSDecimal and NSDecimalNumber whenever you need exact accuracy, especially for any accounting/financial stuff. For anything else that requires accuracy, but for which you don't need exact precision, use a double, not a float. NSNumber is simply an Obj-C object wrapper for number primitives.

For formatting and rounding, look at NSNumberFormatter. Any C primitive number can be wrapped up into an NSNumber, then you can use an NSNumberFormatter to convert it into an NSString. It might seem somewhat clumsy at first, but in real world use, it works pretty well, since you get a very high level of formatting control.

Nick Forge
+1  A: 

You could probably use theValue - fmod(theValue, 0.01) to round to, for this example, the nearest 0.01.) But be warned!

Computers don't actually work with decimal numbers--i.e. numbers in base ten, the way we use them in normal life. They work with binary numbers, and that includes floating-point numbers. They're represented in binary as well, and that means that "number of decimal places" is not always a meaningful property of a floating-point number.

For instance, a floating-point number cannot exactly represent 0.1, and you'll get something like 0.1000000001 if you try to use it in your code. The exact value you get varies by implementation, and is not correctable by subtracting the difference, as the computer can't tell that there is a difference--that's as close as it can get to 0.1.

This is why classes like NSDecimalNumber exist. They simulate decimal mathematics to give values that seem more sane to us. They're prone to many of the same problems as floating-point, but with different values (1.0 / 3.0 is not exactly representable in decimal, for instance, but it's still a discrete value.)

So yes, if your numbers must be rounded to a particular number of decimal places, you need to use NSDecimalNumber for most decimal operations. If it's just for display, you can use something like NSNumberFormatter instead to get the display value without modifying the number itself in memory.

The second part of your question--storing numeric values in user defaults, is easily solved. NSNumber "wraps" a primitive numeric type, but it can be unwrapped:

NSNumber *n = [NSNumber numberWithDouble: 1.234567890];
double aDouble = [n doubleValue];
Jonathan Grynspan
Thanks! Actually both answers make sense. Currently it seems that wraping primitive in NSNumber for persisting is the best solution. However, I would still like to avoid NSDecimaNumber, because of readability of code (I'm still new to Cocoa...). Perhaps writing small method for bankers rounding for double primitive would be solutions of choice?
Mladen Despotovic
Do not use floating point numbers to do arithmetic with currencies.
JeremyP
Should I use double or NSDecimalNumber for this purpose instead?
Mladen Despotovic
When I said "do not use floating point numbers" I meant *any* floating point type - ie. float or double or even long double. The problem is that human currency tends to work in base 10 but floating point types work in base 2. This means that a number of common currency values are impossible to represent exactly as floating point e.g. 10 cents. Either use 64 bit integers to represent the number of cents/pence or use NSDecimalNumber.
JeremyP
Even so, you are still going to have to decide what to do about rounding errors, just like in real life e.g. if you divide £1 among nine people, everybody gets 11p but there is 1p left over that you have to decide to do something with.
JeremyP