How to display currency without rounding as string in Xcode? - iphone

I have trouble when I have currency value
999999999999999999.99
From that value, I want to display it as String. The problem is that value always rounded to
1000000000000000000.00
I'm not expect that value rounded. I want the original value displayed. Do you have any idea how to solve this problem? I tried this code from some answer/turorial in stackoverflow.com :
NSMutableString *aString = [NSMutableString stringWithCapacity:30];
NSNumberFormatter *aCurrency = [[NSNumberFormatter alloc]init];
[aCurrency setFormatterBehavior:NSNumberFormatterBehavior10_4];
[aCurrency setNumberStyle:NSNumberFormatterCurrencyStyle];
[aCurrency setMinimumFractionDigits:2];
[aCurrency setMaximumFractionDigits:2];
[aString appendString:[aCurrency stringFromNumber:productPrice]];
//productPrice is NSDecimalNumber which is have value 999999999999999999.99
cell.textLabel.text = aString;
NSLog(#"the price = %#",cell.textLabel.text);
//it prints $1,000,000,000,000,000,000.00
[aCurrency release];

Unless you have very good reasons not to, it is generally best to keep currency values in a fixed-point format, rather than floating point. Ada supports this directly, but for C-ish languages what you do is keep the value in units of pennies, rather than dollars, and only do the conversion whenever you go to display it.
So in this case the value of productPrice would be 99999999999999999999 (cents). To display it, you'd do something like this (If this were C. I don't know the language):
int const modulus = productPrice % 100;
printf ("The price = %d.%d\n", (int) ((productPrice - modulus) / 100), modulus);
I'd also use an integer rather than a floating point variable to keep the value in almost all cases. It won't work in this case (even if you use a 64-bit integer) because your value is mind-bogglingly large. We're talking 1 million times larger than the US National Debt! If a dollar value that large ever makes sense for anything in my lifetime, we are all in big trouble.

Have you tried these:
* – setRoundingBehavior:
* – roundingBehavior
* – setRoundingIncrement:
* – roundingIncrement
* – setRoundingMode:
* – roundingMode

Related

Convert float to float and truncate after two decimal places Objective-C

I have a float var1 = cashInHandAmount = 4.73000002
I simply want as:
var2 = 4.73.
I have tried like this:
NSString *floatString = [NSString stringWithFormat:#"%.02f",cashInHandAmount];//it prints 4.73
[self.editcash setMaxValue:[floatString floatValue]];//but it again sets 4.73000002 why?
can you guys please help me regarding this?
%f simply rounds for the output. Transforming a float value into a string and back does not work, if the exact value (i. e. 4.73) has no representation in the float format. So transforming it back will "round" the stored value 4.73 into the float format, which is obviously 4.730…02.
You should rarely use (binary, IEEE) floats for financial calculating. Financial values (amount of money) is in most cases an integral value of cents, but no float value of dollars (or whatever). Additionally you can think about using NSDecimal and NSDecimalNumber to ensure, that every value with two digits of precision is storable in the format.
Edit:
float f = 4.73000002;
float rounded = roundf(f * 100.0f) / 100.0f;
NSLog(#"%10.10f", rounded);
outputs:
2014-07-01 10:25:48.653 xctest[596:303] 4.7300000191
It is difficult to check, but probably float can not represent 4.73 exactly. The nearest representable value is 4.7300000191. This is what I said: A rounded decimal representation is not always a possible "binary float" representation. You will face that problem with many values.
Try this :
float var1 = 4.73000002;
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
formatter.maximumFractionDigits = 2; //Set number of fractional digits
NSNumber *num = [NSNumber numberWithFloat:var1];
NSString *roundedNum = [formatter stringFromNumber:num];
DLOG(#"Answer : %#", roundedNum);
If you want truncate all other decimals you can declare a simple method like this:
- (float)sanitizeFloat:(float)value
{
float newValue = [[NSString stringWithFormat:#"%.2f",value] floatValue];
return newValue;
}
and then
float aFloat = 45.070809;
NSLog(#"%f",[self sanitizeFloat:aFloat]);
In your case:
[self.editcash setMaxValue:[self sanitizeFloat:cashInHandAmount]];
The output will be 45.070000
If you're working with monetary amounts that have fixed decimal places, you should always use NSDecimal or NSDecimalNumber - you shouldn't use floats at all, as they are inexact.
You could use:
-[NSDecimalNumber decimalNumberWithString:]
to create your number, and there are many methods for working with NSDecimalNumbers - see Apple's docs or NSDecimalNumber.h for details.

Removing characters after the decimal point for a double

How can I remove the all the characters after the decimal point.
Instead of 7.3456, I would just like 7.
This is what I do to get the number so far with decimal places.
[NSString stringWithFormat:#" %f : %f",(audioPlayer.currentTime),(audioPlayer.duration) ];
Many Thanks,
-Code
You can specify what you want using format string :
[NSString stringWithFormat:#" %.0f : %.0f", (audioPlayer.currentTime),
(audioPlayer.duration)];
If you want this for display, use an NSNumberFormatter:
double sevenpointthreefourfivesix = 7.3456;
NSNumberFormatter * formatter = [[NSNumberFormatter alloc] init];
[formatter setMaximumFractionDigits:0];
NSLog(#"%#", [formatter stringFromNumber:[NSNumber numberWithDouble:sevenpointthreefourfivesix]]);
2011-12-20 20:19:48.813 NoDecimal[55110:903] 7
If you want a value without the fractional part, use round(). If you want the closest integer value not greater than the original value, use floor().
floorf() is the function you're looking for.
you are after
[NSString stringWithFormat:#" %.00f : %.00f",(audioPlayer.currentTime),(audioPlayer.duration) ];
When formatting float you can tell the precision by the number before the f
Cast to int:
[NSString stringWithFormat:#" %i : %i",(int)(audioPlayer.currentTime),(int)(audioPlayer.duration) ];
Casting like this always rounds down (eg: just removes everything after the decimal place). This is what you asked for.
In the case of rounding to the NEAREST whole number you want to add 0.5 to the number
[NSString stringWithFormat:#" %i : %i",(int)(audioPlayer.currentTime+0.5f),(int)(audioPlayer.duration+0.5f) ];
This will round to the nearest whole number. eg: 1.2 becomes 1.7 and casting to int makes 1. 3.6 becomes 4.1 and casting makes 4. :)
Why not just cast the audioPlayer.currentTime to an integer before you use stringWithFormat?
[NSString stringWithFormat:#"%d", (int)(audioPlayer.currentTime)];
All you need to do is type-cast the double to an int, like so: int currentTime_int = (int)audioPlayer.currentTime;.
You can use this same approach for the other variable.
Many of the shorter answers here will work correctly. But if you want your code to be really clear and readable, you might want to explicitly specify your desired conversion from float to int, such as using:
int tmpInt = floorf(myFloat); // or roundf(), etc.
and then separately specifying how you want the integer formated, e.g.
... stringWithFormat:#"%d", tmpInt ... // or #"%+03d", etc.
instead of assuming that an inline cast shows what you want.
You may also use
double newDvalue =floor(dValue);
it will remove all the decimals point
using %.0f for string format will be good also

Trying to save long long into NSNumber from String

I am trying to save a long long number (received as a string) such as '80182916772147201' into an NSNumber.
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterBehaviorDefault];
[item setObject:[f numberFromString:#"80182916772147201"] forKey:#"theID"];
[f release];
When I NSLog this out, assuming the string was '80182916772147201' I get:
NSLog(#"%lld", [[item objectForKey:#"theID"] longLongValue]);
Returns: '80182916772147200' - Note the rounded down final digit.
What am I doing wrong?
The problem is that NSNumberFormatter has decided to represent that number as a floating-point number. To force it to use integers only:
[f setAllowsFloats:NO];
Can you try this?
NSString *numStr = [NSString stringWithFormat:#"%llu", [myNum unsignedLongLongValue]];
This makes a few reasonable assumptions such as numStr will only contain numeric digits and it contains a 'valid' unsigned long long value. A drawback to this approach is that UTF8String creates what essentially amounts to [[numStr dataUsingEncoding:NSUTF8StringEncoding] bytes], or in other words something along the lines of 32 bytes of autoreleased memory per call. For the vast majority of uses, this is no problem what-so-ever.
For an example of how to add something like unsignedLongLongValue to NSString that is both very fast and uses no autoreleased memory as a side effect, take a look at the end of my (long) answer to this SO question. Specifically the example implementation of rklIntValue, which would require only trivial modifications to implement unsignedLongLongValue.

Bug with iPhone Currency for Euro format with NSDecimalNumber using NSLocale?

My app uses monetary values, and I would like to represent these values in the currency and formats that the user has set on their iPhone however, when testing, I seem to be having troubles with the German EURO format.
I have a method that returns a decimal number (for storing in core data) from the currency formatted string. The string was actually formatted by another (similar) method using NSlocale.
I have tested with UK, US, Japanese and German locales, so far only Germany has caused any problems.
Here's the code, the string being passed is for example: 2,56 €
+ (NSDecimalNumber*)TLCurrencyFormatFromString:(NSString*)strNumber {
NSNumberFormatter *fmtrCurrencyFromStr = nil;
fmtrCurrencyFromStr = [[NSNumberFormatter alloc] init];
[fmtrCurrencyFromStr setFormatterBehavior:NSNumberFormatterBehavior10_4];
[fmtrCurrencyFromStr setLocale:[NSLocale currentLocale]];
[fmtrCurrencyFromStr setNumberStyle:NSNumberFormatterCurrencyStyle];
[fmtrCurrencyFromStr setGeneratesDecimalNumbers:YES];
NSLog(#"Currency Separator: %#", [fmtrCurrencyFromStr decimalSeparator]); //prints a comma
NSLog(#"Currency Code: %#", [fmtrCurrencyFromStr currencyCode]); //prints EUR
int currencyScale = [fmtrCurrencyFromStr maximumFractionDigits];
NSDecimalNumberHandler *roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:currencyScale raiseOnExactness:FALSE raiseOnOverflow:TRUE raiseOnUnderflow:TRUE raiseOnDivideByZero:TRUE];
NSDecimalNumber* currencyNumber = [[NSDecimalNumber alloc] initWithDecimal:[[fmtrCurrencyFromStr numberFromString:strNumber] decimalValue]];
NSLog(#"Currency Number = %#", [currencyNumber decimalNumberByRoundingAccordingToBehavior:roundingBehavior]);
return [currencyNumber decimalNumberByRoundingAccordingToBehavior:roundingBehavior];
}
The 'currencyNumber' variable gets printed as: -18446912453607470532000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
whereas in all the other locales, it would print 2.56 for storing into Core Data as NSDecimalNumber.
Setting the property:
[fmtrCurrencyFromStr setLenient:YES];
Fixes the problem for France, Germany, Norway, UK, US, Japan, Nambia etc... I can't say it fixes it for all because I haven't tested all, but of all those that I've tested so far, it has worked.
I think your problem is with the line
NSDecimalNumber* currencyNumber = [[NSDecimalNumber alloc] initWithDecimal:[[fmtrCurrencyFromStr numberFromString:strNumber] decimalValue]];
Try replacing it with the mostly-equivalent line
NSDecimalNumber * currencyNumber = [fmtrCurrencyFromStr numberFromString:strNumber];
You've set generatesDecimalNumbers, so the two are pretty much equivalent except for one annoying case: nil.
Sending a message to nil is guaranteed to do nothing and return 0 for most standard types (integer (except not 64-bit integers on old 32-bit runtimes), floating point, pointer types); it "returns" uninitialized memory if the method returns a struct. NSDecimal is a struct.
(The underlying reason is essentially that the runtime doesn't know how big the struct is, so doesn't know how much memory to zero, hence you get "uninitialized" memory instead. I usually hit this one when I do v.frame without ensuring that v is not nil.)
EDIT: Of course, you other problem is why NSNumberFormatter isn't parsing the price correctly. That's a tough one. I'd try stripping the currency symbol.
I verified that on iOS 4.3 using the [formatter setLenient:YES] flag worked for me as well using Euros. Without that flag set, the formatter does not return correct values for Euros.

Obtaining an NSDecimalNumber from a locale specific string?

I have some string s that is locale specific (eg, 0.01 or 0,01). I want to convert this string to a NSDecimalNumber. From the examples I've seen thus far on the interwebs, this is accomplished by using an NSNumberFormatter a la:
NSString *s = #"0.07";
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[formatter setGeneratesDecimalNumbers:YES];
NSDecimalNumber *decimalNumber = [formatter numberFromString:s];
NSLog([decimalNumber stringValue]); // prints 0.07000000000000001
I'm using 10.4 mode (in addition to being recommended per the documentation, it is also the only mode available on the iPhone) but indicating to the formatter that I want to generate decimal numbers. Note that I've simplified my example (I'm actually dealing with currency strings). However, I'm obviously doing something wrong for it to return a value that illustrates the imprecision of floating point numbers.
What is the correct method to convert a locale specific number string to an NSDecimalNumber?
Edit: Note that my example is for simplicity. The question I'm asking also should relate to when you need to take a locale specific currency string and convert it to an NSDecimalNumber. Additionally, this can be expanded to a locale specific percentage string and convert it to a NSDecimalNumber.
Years later:
+(NSDecimalNumber *)decimalNumberWithString:(NSString *)numericString in NSDecimalNumber.
Based on Boaz Stuller's answer, I logged a bug to Apple for this issue. Until that is resolved, here are the workarounds I've decided upon as being the best approach to take. These workarounds simply rely upon rounding the decimal number to the appropriate precision, which is a simple approach that can supplement your existing code (rather than switching from formatters to scanners).
General Numbers
Essentially, I'm just rounding the number based on rules that make sense for my situation. So, YMMV depending on the precision you support.
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[formatter setGeneratesDecimalNumbers:TRUE];
NSString *s = #"0.07";
// Create your desired rounding behavior that is appropriate for your situation
NSDecimalNumberHandler *roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:2 raiseOnExactness:FALSE raiseOnOverflow:TRUE raiseOnUnderflow:TRUE raiseOnDivideByZero:TRUE];
NSDecimalNumber *decimalNumber = [formatter numberFromString:s];
NSDecimalNumber *roundedDecimalNumber = [decimalNumber decimalNumberByRoundingAccordingToBehavior:roundingBehavior];
NSLog([decimalNumber stringValue]); // prints 0.07000000000000001
NSLog([roundedDecimalNumber stringValue]); // prints 0.07
Currencies
Handling currencies (which is the actual problem I'm trying to solve) is just a slight variation on handling general numbers. The key is that the scale of the rounding behavior is determined by the maximum fractional digits used by the locale's currency.
NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[currencyFormatter setGeneratesDecimalNumbers:TRUE];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
// Here is the key: use the maximum fractional digits of the currency as the scale
int currencyScale = [currencyFormatter maximumFractionDigits];
NSDecimalNumberHandler *roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:currencyScale raiseOnExactness:FALSE raiseOnOverflow:TRUE raiseOnUnderflow:TRUE raiseOnDivideByZero:TRUE];
// image s is some locale specific currency string (eg, $0.07 or €0.07)
NSDecimalNumber *decimalNumber = (NSDecimalNumber*)[currencyFormatter numberFromString:s];
NSDecimalNumber *roundedDecimalNumber = [decimalNumber decimalNumberByRoundingAccordingToBehavior:roundingBehavior];
NSLog([decimalNumber stringValue]); // prints 0.07000000000000001
NSLog([roundedDecimalNumber stringValue]); // prints 0.07
This seems to work:
NSString *s = #"0.07";
NSScanner* scanner = [NSScanner localizedScannerWithString:s];
NSDecimal decimal;
[scanner scanDecimal:&decimal];
NSDecimalNumber *decimalNumber = [NSDecimalNumber decimalNumberWithDecimal:decimal];
NSLog([decimalNumber stringValue]); // prints 0.07
Also, file a bug on this. That's definitely not the correct behavior you're seeing there.
Edit: Until Apple fixes this (and then every potential user updates to the fixed OSX version), you're probably going to have to roll your own parser using NSScanner or accept 'only' double accuracy for entered numbers. Unless you're planning to have the Pentagon budget in this app, I'd suggest the latter. Realistically, doubles are accurate to 14 decimal places, so at anything less than a trillion dollars, they'll be less than a penny off. I had to write my own date parsing routines based on NSDateFormatter for a project and I spent literally a month handling all the funny edge cases, (like how only Sweden has the day of week included in its long date).
See also Best way to store currency values in C++
The best way to handle currency is to use an integer value for the smallest unit of the currency, i.e. cents for dollars/euros, etc. You'll avoid any floating point related precision errors in your code.
With that in mind, the best way to parse strings containing a currency value is to do it manually (with a configurable decimal point character). Split the string at the decimal point, and parse both the first and second part as integer values. Then use construct your combined value from those.