Prevent scientific notation with NSDecimalNumber output as NSString - objective-c

I have a problem similar to the one in this question: How to obtain an unformatted string representation of an NSDecimal or NSDecimalNumber? I need to have a number in string format accurately represented as an NSNumber, but this number is converted back to a string in other places, and I can't avoid that. The problem I'm having is that when the number is converted back to a string, the string is in scientific notation in some cases.
NSDecimalNumber *dn = [NSDecimalNumber decimalNumberWithString:#"0.0001"];
NSString *s = [dn stringValue]; // s will be #"1E-4"
How can I prevent this number from being displayed in scientific notation?
I am working in a circa 2005 version of GNUstep (possibly v1.11.1 for GNUstep Base), rather than on mac or iPhone, which may account for some of the differences in behavior vs the other question I referenced. I have looked over the base library API and the documentation in my own GNUstep install, but I can't seem to find anything to help.
EDIT 2/7/12:
The question has changed slightly, but the goal is still the same. Originally, I didn't think I was able to control the string output piece, but I can pass the value back as a string. At this point I am attempting to use a formatting string, but I still want to prevent the scientific notation from appearing.
[NSString stringWithFormat:#"%1.14g", [val doubleValue]]
I've chosen to use %g because we would like a specific number of significant digits for the value. If I use %f, I can trim the extra zeros, but the number does not always come out cleanly. 800000000.79 appears as 800000000.7899999600, for example.
Is there a way to get a cleanly formatted number with up to a certain number of significant digits (or decimal places) without displaying scientific notation before that number of digits?
I'm willing to accept C advice as well.

You should check out the NSNumberFormatter
// Create formatter
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle]; // adjust this
NSString *formattedOutput = [formatter stringFromNumber:yourDecimalNumber];

How about getting the the C value (double, int, long, etc.) and then format it as a C string, or as an NSString with stringWithFormat:?

Try printing the number using NSNumberFormatter instead of the stringValue method. It has a lot more options.
(I'm assuming NSNumberFormatter is available on GNUstep)

Use the below methods to avoid scientific exponent notation representation.
Convert Double to String
func getStringFrom(double doubleVal: Double) -> String
{
var stringValue : String = "0.00"
let formatter = NSNumberFormatter()
formatter.usesSignificantDigits = true;
formatter.maximumSignificantDigits = 100
formatter.groupingSeparator = "";
formatter.numberStyle = .DecimalStyle
stringValue = formatter.stringFromNumber(doubleVal)!;
return stringValue
}
Convert String to Double
func getDoubleFrom(textField textField: UITextField) -> Double
{
var doubleValue : Double = 0.0
if let val = textField.text
{
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = NSNumberFormatterStyle.DecimalStyle
let finalNumber = numberFormatter.numberFromString(val)
doubleValue = (finalNumber?.doubleValue)!;
}
return doubleValue
}

Related

Convert back localized NSString number (> 4 digits) to integer

I used the localizedStringWithFormat: method on NSString class to convert a seven digit integer number to an NSString somewhere in my code and need to convert it back to an integer now.
As my App is localized for different regions with different separators after three digits (e.g. '.' in the U.S. and ',' in Germany), what's the best way to convert a localized NSString integer value to an integer?
I tried integerValue on my string as follows but it didn't work:
// Somewhere in code:
int num = 1049000;
NSString *myLocalizedNumString = [NSString localizedStringWithFormat:#"%d", num];
// myLocalizedNumString (U.S.): '1,049,000'
// myLocalizedNumString (Germany): '1.049.000'
// Somewhere else where I have a reference to my string but none to the num:
int restoredNum = [myLocalizedNumString integerValue];
// restoredNum isn't 1049000 (it's 0, the initial value)
What would be a good working way of doing it?
Despite its name NSNumberFormatter converts both ways, it is also a string parser. Using the method numberFromString after setting the number formatter’s numberStyle property to NSNumberFormatterDecimalStyle solves your problem.
The code might look as follows:
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
NSInteger restoredNum = [[formatter numberFromString:myLocalizedNumString] integerValue];

How do I force a sign-character on the output of an NSNumberFormatter

I want to use a number formatter to generate my output, so the number is automatically formatted for the user's locale, but I want it to work like "%+.1f" does in printf(), that is always have a sign specified.
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.numberStyle = NSNumberFormatterDecimalStyle;
nf.maximumFractionDigits = 1;
double val = 3.1234;
label.text = [NSString stringWithFormat: #"XXX %# XXX", [nf stringFromNumber: [NSNumber numberWithDouble: val]]];
I want the label to come out "XXX +3.1 XXX" in the US and the appropriate but equivalent string for any other location. The only things I can find are setPositiveFormat: and setPositivePrefix:.
But I don't want to set the format since I don't know how to format numbers in other countries; I don't know if a plus-sign is used to designate a positive number in Arabic or Russian or some culture I have not thought of. I do know, for example, that decimal points, commas, spaces, etc., all have different meanings in European countries compared to the U.S. - Could the same be true for +/- signs?
What I do currently is:
label.text = [NSString stringWithFormat: #"XXX %s%# XXX", (val < 0) ? "" : "+",
[nf stringFromNumber: [NSNumber numberWithDouble: val]]];
But this presumes that '+' and '-' are correct for all formats.
I'm sure it must be there since it is a standard formatting thing that has been in printf() since the dark ages...
How about this:
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.numberStyle = NSNumberFormatterDecimalStyle;
nf.maximumFractionDigits = 1;
double val = 3.1234;
NSString *sign = (val < 0) ? [nf minusSign] : [nf plusSign];
NSString *num = [nf stringFromNumber:#(abs(val))]; // avoid double negative
label.text = [NSString stringWithFormat: #"XXX %#%# XXX", sign, num];
You may need to check to see if num has the sign prefix or not so it isn't shown twice.
Edit: After some playing around, it has been determined, for the "Decimal" style, that no current locale uses a positivePrefix. No current locale uses a plusSign other than the standard + character. No current locale uses a negativePrefix that is different than minusSign. No current locale uses either positiveSuffix or negativeSuffix.
So an easier approach would be to do:
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.numberStyle = NSNumberFormatterDecimalStyle;
nf.maximumFractionDigits = 1;
[nf setPositivePrefix:[nf plusSign]];
[nf setNegativePrefix:[nf minusSign]];
label.text = [nf stringFromNumber:#(val)];
This case it's simple, just add the prefix:
nf.positivePrefix= nf.plusSign;
Though it won't use the user's locale, you can do the following to generate the +/- sign without the somewhat expensive overhead of an NSNumberFormatter:
// assume 'number' is an NSNumber
label.text = [NSString stringWithFormat:#"%+.02f", [number floatValue]];
Simple Case:
let f = NumberFormatter()
f.positivePrefix = f.plusSign
Currency Case :
Hack needed, because setting the prefix to plusSign only will remove the currency symbol.
let f = NumberFormatter()
f.numberStyle = .currency
f.positivePrefix = f.plusSign + f.currencySymbol
There is a bit more work depending on the locale.. The currency symbol may be before, or after, but this is probably another subject..
Edit:
Even if it is another subject, I'd say a possible solution to the problem above is to subclass NSNumberFormatter :
override func string(from number: NSNumber) -> String? {
returns ( number.doubleValue >= 0 ? super.plusSign : "" ) + super.string(from: number)
}
This way, NSNumberFormatter should manage the currency position while your subclass simply prepend the + sign. No time to test this in depth, but at least it is an approach.
The underlying formatting language for NSNumberFormatter doesn't have any provision for what you want to do -- it will allow you to specify a localized positive sign on exponents, but not for the entire formatted string. Nor does NSLocale seem to make available the localized positive sign.
Aside from making a dummy string that includes an exponent, pulling the localized positive sign out, and putting your final formatted string together by hand, I think you're out of luck.
A reusable formatter in swift:
var numberFormatter: NSNumberFormatter {
let formatter = NSNumberFormatter()
formatter.numberStyle = .DecimalStyle
formatter.locale = NSLocale(localeIdentifier: "it_IT")//your Locale
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 0
formatter.positivePrefix = formatter.plusSign
return formatter
}
Then use it:
let myDoubleValue = 12.00
let myStringNumber = numberFormatter.stringFromNumber(myDoubleValue)!
I don't think any of the previous answers will actually take into consideration everything you mentioned in your question.
It is true that NumberFormatter does not have an option to set the plus sign visible for all positive numbers when formatting currency values.
Also, replacing prefixes and suffixes will likely break the format for some regions and always replacing a prefix will only work if the set locale uses the currency symbol on the left.
A simple way to address this without losing the locale formatting can be seen below:
var value: Double = 3.1234
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.maximumFractionDigits = 1
if value > 0 {
return formatter.string(for: value.negated())?.replacingOccurrences(
of: formatter.minusSign,
with: formatter.plusSign
)
} else {
return formatter.string(for: value)
}
Even though this can be seen as hack, it's an effective way to achieve everything you mentioned without manually writing a number formatter.

How do I convert an integer to the corresponding words in objective-c?

Is there a way in Objective-C on iOS to spell out an integer number as text?
For example, if I have
NSInteger someNumber = 11242043;
I would like to know some function so that would return a string similar to "eleven million two hundred forty two thousand forty three."
Apple has a lot of handy formatting functionality built in for many data types. Called a "formatter," they can convert objects to/from string representations.
For your case, you will be using NSNumberFormatter, but if you have an integer you need to convert it to an NSNumber first. See below example.
NSInteger anInt = 11242043;
NSString *wordNumber;
//convert to words
NSNumber *numberValue = [NSNumber numberWithInt:anInt]; //needs to be NSNumber!
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterSpellOutStyle];
wordNumber = [numberFormatter stringFromNumber:numberValue];
NSLog(#"Answer: %#", wordNumber);
// Answer: eleven million two hundred forty-two thousand forty-three
If you'd like to learn more about formatters:
https://developer.apple.com/library/content/documentation/General/Conceptual/Devpedia-CocoaApp/Formatter.html
Power of extension for Swift 5
import Foundation
public extension Int {
var asWord: String? {
let numberValue = NSNumber(value: self)
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
return formatter.string(from: numberValue)
}
}
var value = 2
if let valueAsWord = value.asWord {
//do something with your worded number here
print("value worded = \(valueAsWord)")
} else {
print("could not word value :(")
}
Note: Edited to protect against formatter.string(from: returning nil which is highly not likely, but still possible.
Output:
value worded = two
From the docs:
NSNumberFormatterSpellOutStyle
Specifies a spell-out format; for example, “23” becomes “twenty-three”.
Available in iOS 2.0 and later.
Declared in NSNumberFormatter.h.
As your question isn't very specific, I won't post full-fledged code source either.
With Swift 5 / iOS 12.2, NumberFormatter has a numberStyle property that can be set with value NumberFormatter.Style.spellOut. spellOut has the following declaration:
case spellOut = 5
A style format in which numbers are spelled out in the language defined by the number formatter locale.
For example, in the en_US locale, the number 1234.5678 is represented as one thousand two hundred thirty-four point five six seven eight; in the fr_FR locale, the number 1234.5678 is represented as mille deux cent trente-quatre virgule cinq six sept huit.
This style is supported for most user locales. If this style doesn't support the number formatter locale, the en_US locale is used as a fallback.
The Playground code below shows how to convert an integer to a spell-out text using NumberFormatter spellOut style:
import Foundation
let integer = 2018
let formatter = NumberFormatter()
formatter.numberStyle = NumberFormatter.Style.spellOut
let spellOutText = formatter.string(for: integer)!
print(spellOutText) // prints: two thousand eighteen
We can do this in swift like this.
let formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle. SpellOutStyle
println("\(identifier) \(formatter.stringFromNumber(1234.5678))")
You can use the below function to convert an integer to words using swift native number style.
func toWords<N>(number: N) -> String? {
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
switch number {
case is Int, is UInt, is Float, is Double:
return formatter.string(from: number as! NSNumber)
case is String:
if let number = Double(number as! String) {
return formatter.string(from: NSNumber(floatLiteral: number))
}
default:
break
}
return nil
}
print(toWords(number: 12312))
print(toWords(number: "12312"))
For my own reference, this is #moca's answer, but ready for use:
- (NSString *) spellInt:(int)number {
NSNumber *numberAsNumber = [NSNumber numberWithInt:number];
NSNumberFormatter *formatter = [NSNumberFormatter new];
[formatter setNumberStyle:NSNumberFormatterSpellOutStyle];
return [formatter stringFromNumber:numberAsNumber];
}
Note: This is using ARC.

parsing string into different kind of number string

I have a string called realEstateWorth with a value of $12,000,000.
I need this same string to remain a string but for any number (such as the one above) to be displayed as $12 MILLION or $6 MILLION. The point is it needs the words "MILLION" to come after the number.
I know there is nsNumberFormatter that can convert strings into numbers and vice versa but can it do what I need?
If anyone has any ideas or suggestions, it would be much appreciated.
Thank you!
So as I see it, you have two problems:
You have a string representation of something that's actually a number
You (potentially) have a number that you want formatted as a string
So, problem #1:
To convert a string into a number, you use an NSNumberFormatter. You've got a pretty simple case:
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *n = [f numberFromString:#"$12,000,000"];
// n is 12000000
That was easy! Now problem #2:
This is trickier, because you want a mixed spell-out style. You could consider using an NSNumberFormatter again, but it's not quite right:
[f setNumberStyle:NSNumberFormatterSpellOutStyle];
NSString *s = [f stringFromNumber:n];
// s is "twelve million"
So, we're closer. At this point, you could perhaps maybe do something like:
NSInteger numberOfMillions = [n integerValue] / 1000000;
if (numberOfMillions > 0) {
NSNumber *millions = [NSNumber numberWithInteger:numberOfMillions];
NSString *numberOfMillionsString = [f stringFromNumber:millions]; // "twelve"
[f setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString *formattedMillions = [f stringFromNumber:millions]; // "$12.00"
if ([s hasPrefix:numberOfMillionsString]) {
// replace "twelve" with "$12.00"
s = [s stringByReplacingCharactersInRange:NSMakeRange(0, [numberOfMillionsString length]) withString:formattedMillions];
// if this all works, s should be "$12.00 million"
// you can use the -setMaximumFractionDigits: method on NSNumberFormatter to fiddle with the ".00" bit
}
}
However
I don't know how well this would work in anything other than english. CAVEAT IMPLEMENTOR
Worst case scenario, you could implement a category on NSString to implement the behaviour you want.
In the method that you would do in that category you could take an NSNumberFormatter to bring that string to a number and by doing some modulo operation you could define if you need the word Million, or Billion, etc. and put back a string with the modulo for Million or other way you need it to be.
That way you could just call that method on your NSString like this :
NSString *humanReadable = [realEstateWorth myCustomMethodFromMyCategory];
And also.
NSString are immutable, so you can't change it unless you assign a new one to your variable.
I'd recommend storing this value as an NSNumber or a float. Then you could have a method to generate an NSString to display it like:
- (NSString*)numberToCurrencyString:(float)num
{
NSString *postfix = #"";
if (num > 1000000000)
{
num = num / 1000000000;
postfix = #" Billion";
}
else if (num > 1000000)
{
num = num / 1000000;
postfix = #" Million";
}
NSString *currencyString = [NSString stringWithFormat:#"%.0f%#", num, postfix];
return currencyString;
}
Note: Your question states that your input needs to remain a string. That's fine. So you'd need to 1.) first parse the number out of the string and 2.) then reconvert it to a string from a number. I've shown how to do step 2 of this process.

How to convert a string into double and vice versa?

I want to convert a string into a double and after doing some math on it, convert it back to a string.
How do I do this in Objective-C?
Is there a way to round a double to the nearest integer too?
You can convert an NSString into a double with
double myDouble = [myString doubleValue];
Rounding to the nearest int can then be done as
int myInt = (int)(myDouble + (myDouble>0 ? 0.5 : -0.5))
I'm honestly not sure if there's a more streamlined way to convert back into a string than
NSString* myNewString = [NSString stringWithFormat:#"%d", myInt];
To really convert from a string to a number properly, you need to use an instance of NSNumberFormatter configured for the locale from which you're reading the string.
Different locales will format numbers differently. For example, in some parts of the world, COMMA is used as a decimal separator while in others it is PERIOD — and the thousands separator (when used) is reversed. Except when it's a space. Or not present at all.
It really depends on the provenance of the input. The safest thing to do is configure an NSNumberFormatter for the way your input is formatted and use -[NSFormatter numberFromString:] to get an NSNumber from it. If you want to handle conversion errors, you can use -[NSFormatter getObjectValue:forString:range:error:] instead.
Adding to olliej's answer, you can convert from an int back to a string with NSNumber's stringValue:
[[NSNumber numberWithInt:myInt] stringValue]
stringValue on an NSNumber invokes descriptionWithLocale:nil, giving you a localized string representation of value. I'm not sure if [NSString stringWithFormat:#"%d",myInt] will give you a properly localized reprsentation of myInt.
Here's a working sample of NSNumberFormatter reading localized number String (xCode 3.2.4, osX 10.6), to save others the hours I've just spent messing around. Beware: while it can handle trailing blanks such as "8,765.4 ", this cannot handle leading white space and this cannot handle stray text characters. (Bad input strings: " 8" and "8q" and "8 q".)
NSString *tempStr = #"8,765.4";
// localization allows other thousands separators, also.
NSNumberFormatter * myNumFormatter = [[NSNumberFormatter alloc] init];
[myNumFormatter setLocale:[NSLocale currentLocale]]; // happen by default?
[myNumFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
// next line is very important!
[myNumFormatter setNumberStyle:NSNumberFormatterDecimalStyle]; // crucial
NSNumber *tempNum = [myNumFormatter numberFromString:tempStr];
NSLog(#"string '%#' gives NSNumber '%#' with intValue '%i'",
tempStr, tempNum, [tempNum intValue]);
[myNumFormatter release]; // good citizen
olliej's rounding method is wrong for negative numbers
2.4 rounded is 2 (olliej's method gets this right)
−2.4 rounded is −2 (olliej's method returns -1)
Here's an alternative
int myInt = (int)(myDouble + (myDouble>0 ? 0.5 : -0.5))
You could of course use a rounding function from math.h
// Converting String in to Double
double doubleValue = [yourString doubleValue];
// Converting Double in to String
NSString *yourString = [NSString stringWithFormat:#"%.20f", doubleValue];
// .20f takes the value up to 20 position after decimal
// Converting double to int
int intValue = (int) doubleValue;
or
int intValue = [yourString intValue];
For conversion from a number to a string, how about using the new literals syntax (XCode >= 4.4), its a little more compact.
int myInt = (int)round( [#"1.6" floatValue] );
NSString* myString = [#(myInt) description];
(Boxes it up as a NSNumber and converts to a string using the NSObjects' description method)
For rounding, you should probably use the C functions defined in math.h.
int roundedX = round(x);
Hold down Option and double click on round in Xcode and it will show you the man page with various functions for rounding different types.
This is the easiest way I know of:
float myFloat = 5.3;
NSInteger myInt = (NSInteger)myFloat;
from this example here, you can see the the conversions both ways:
NSString *str=#"5678901234567890";
long long verylong;
NSRange range;
range.length = 15;
range.location = 0;
[[NSScanner scannerWithString:[str substringWithRange:range]] scanLongLong:&verylong];
NSLog(#"long long value %lld",verylong);
convert text entered in textfield to integer
double mydouble=[_myTextfield.text doubleValue];
rounding to the nearest double
mydouble=(round(mydouble));
rounding to the nearest int(considering only positive values)
int myint=(int)(mydouble);
converting from double to string
myLabel.text=[NSString stringWithFormat:#"%f",mydouble];
or
NSString *mystring=[NSString stringWithFormat:#"%f",mydouble];
converting from int to string
myLabel.text=[NSString stringWithFormat:#"%d",myint];
or
NSString *mystring=[NSString stringWithFormat:#"%f",mydouble];
I ended up using this handy macro:
#define STRING(value) [#(value) stringValue]