In swift I know how to set the number of digits after the decimal point when converting a double to a string:
String(format: "%0.2f", someDouble)
Similarly I know how to set the number of digits before the decimal point:
String(format: "%02d", someDouble)
But how can I do both?
I want the string to always have a 00.00 format.
Thanks
You simply combine the two:
String(format: "%05.2f", someDouble)
The 0 means fill with leading zeros as needed.
The 5 means you want the final output to be at least 5 characters, include the decimal point.
The .2 means you want two decimal places.
If this is a number you are showing to a user then you should probably use NumberFormatter so the decimal is properly formatted for the user's locale.
Related
I want to align numbers by digits in table rows. For example:
___123.4
-5 678.9
That is, so that tens are under tens, units under units, a fractional number under a fractional number.
To convert a Number to a String, I use the numberStringFormatter function.
func numberStringFormat(_ number: Double) -> String {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.maximumFractionDigits = 1
numberFormatter.groupingSeparator = " "
let result = numberFormatter.string(from: NSNumber(value: number))
return result ?? ""
}
This function sets the fractional format, determines the number of decimal places, and groups the numbers before the decimal point by digits.
But if the number is an integer, without fractions, or a digit after a floating point after rounding it turns out to be 0, then the formatted string looks like this, for example, 123
And then these numbers in the rows of the table are shifted and it turns out like this:
----123
5 678.9
That is, the fractional number on the bottom row is under the integer number on the top row.
In my opinion, I can solve this task if I force the Number to always show 0 after converting to a String.
I tried googling but couldn't find an answer to this question.
Maybe someone has already encountered such situations and can suggest a possible solution, or at least in what direction to move?
Or, perhaps there is some other solution without forcing 0 to be shown, but simply aligning the characters vertically?
Any ideas are welcome. I really appreciate your help.
Update: A greate solution from HangarRash.
minimumFractionDigits = 1
I am using String(format:) to convert a Float. I thought the number would be rounded.
Sometimes it is.
String(format: "%.02f", 1.455) //"1.46"
Sometimes not.
String(format: "%.02f", 1.555) //"1.55"
String(round(1.555 * 100) / 100.0) //"1.56"
I guess 1.55 cannot be represented exactly as binary. And that it becomes something like 1.549999XXXX
But NumberFormatter doesn't seem to cause the same problem... Why? Should it be preferred over String(format:)?
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
if let string = formatter.string(for: 1.555) {
print(string) // 1.56
}
Reference to the problem (to use String (format :) to round a decimal number) can be found in the answers (or more often comments) to these questions: Rounding a double value to x number of decimal places in swift and How to format a Double into Currency - Swift 3. But the problem it covers (math with FloatingPoint) has been dealt with many times on SO (for all languages).
String(format:) does not have the function of rounding a decimal number (even if it is unfortunately proposed in some answers) but of formatting it (as its name suggests). This formatting sometimes causes a rounding. That is true. But we have to keep in mind a problem that the number 1.555 is... not worth 1.555.
In Swift, Double and Float, that conform to the FloatingPoint protocol respect the IEEE 754 specification. However, some values cannot be exactly represented by the IEEE 754 standard.
In the same way that you can't represent a third exactly in a (finite) decimal expansion, there are lots of numbers which look simple in decimal, but which have long or infinite expansions in a binary expansion." (source)
To be convinced of this, we can use The Float Converter to convert between the decimal representation of numbers (like "1.02") and the binary format used by all modern CPUs (IEEE 754 floating point). For 1.555, the value actually stored in float is 1.55499994754791259765625
So the problem does not come from String (format :). For example, we can try another way to round to the thousandth and we find the same problem. :
round (8.45 * pow (10.0, 3.0)) / pow (10.0, 3.0)
// 8.449999999999999
That is how it is : "Binary floating point arithmetic is fine so long as you know what's going on and don't expect values to be exactly the decimal ones you put in your program".
So the real question is : is this really a problem for you to use ? It depends on the app. Generally if we convert a number into a String by limiting its precision (by rounding), it is because we consider that this precision is not useful to the user. If this is the kind of data we're talking about, then it's okay to use a FloatingPoint.
However, to format it it may be more relevant to use a NumberFormatter. Not necessarily for its rounding algorithm, but rather because it allows you to locate the format :
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
formatter.locale = Locale(identifier: "fr_FR")
formatter.string(for: 1.55)!
// 1,55
formatter.locale = Locale(identifier: "en_US")
formatter.string(for: 1.55)!
// 1.55
Conversely, if we are in a case where precision matters, we must abandon Double / Float and use Decimal. Still to keep our rounding example, we can use this extension (which may be the best answer to the question "Rounding a double value to x number of decimal places in swift ") :
extension Double {
func roundedDecimal(to scale: Int = 0, mode: NSDecimalNumber.RoundingMode = .plain) -> Decimal {
var decimalValue = Decimal(self)
var result = Decimal()
NSDecimalRound(&result, &decimalValue, scale, mode)
return result
}
}
1.555.roundedDecimal(to: 2)
// 1.56
I have the following in playground:
let a:CGFloat = 1.23
let b:Double = 3.45
let c:Float = 6.78
String(format: "(%2.2f|%2.2f|%2.2f)",a,b,c) // prints "(3.45|6.78|0.00)"
let d = NSMakePoint(9.87,6.54)
String(format: "(%2.2f|%2.2f)",d.x,d.y) // prints "(0.00|0.00)"
So why is c:Float rendered as 0.00. I possibly need something else than f (Apples docu on this formatting function is - to be polite - quite thin with a limes going to zero).
BUT: Why is the CGFloat in the first place rendered correctly while the two CGFloats inside the NSPoint get rendered as 0.00?
And no: it's not a duplicate of Precision String Format Specifier In Swift and its pendant.
P.S.
String(format: "(%2.2f|%2.2f)",Double(d.x),d.y) // prints "(9.87|0.00)"
which is a work around but no explanation.
And a PPS: Isn't %2.2f supposed to print " 9.87" instead of "9.87" (2 places for leading digits? It seems to ignore the number. Specifying %02.2f also prints "9.87" rather than "09.87"
CGFloat isn't a float or a double, it is its own struct. If you want the double or float value of a CGFloat (which is dependent on the architecture being 32 or 64 but, then you can access it with a.native. In other words try:
String(format: "(%2.2f|%2.2f|%2.2f)",a.native,b,c)
You'd see similar behavior if you tried to pass other non-float or non-double arguments to the %f formatter. For example:
var str = "Hello, playground"
String(format: "%2.2f|%2.2f|%2.2f", arguments: [str,b,c])
would result in "3.45|6.78|0.00". It appears to be looking for another float in your arguments to satisfy the last %f, and defaults to 0.00
As for the PPS. %2.2F is two decimal place and at least 2 total digits. If you wanted two digits minimum before the decimal, you'd want %5.2f. 5 because the decimal itself takes a place.
i have a one application i know The range of a double is **1.7E +/- 308 (15 digits).**but in my application i have to devide text box 's value to 100.0 my code is
double value=[strPrice doubleValue]/100.0;
NSString *stramoount=[#"" stringByAppendingFormat:#"%0.2f",value ];
when i devide 34901234566781212 by 100 it give me 349012345667812.12 but when i type
349012345667812124 and devide by 100 it give me by 100 it give me 3490123456678121.00 which is wrong whether i change datatype or how can i change my code
The number 349012345667812124 has 18 decimal digits. the double format only provides slightly less than 16 decimal digits of precision (the actual number is not an integer because the format's binary digits do not correspont directly to decimal ones). Thus it is completely expected that the last 2 or 3 digits cannot be represented accurately, and it already happens when the literal "349012345667812124" is parsed to the double format, before any calculations happen.
The fact that you get the expected result with the number 34901234566781212 means nothing; it just happens to be close enough to the nearest value the double format can represent.
To avoid this problem, use the NSDecimal or NSDecimalNumber types.
Use
NSDecimalNumber * dec=[[NSDecimalNumber decimalNumberWithString:value.text locale: [NSLocale currentLocale]] decimalNumberByDividingBy:[NSDecimalNumber decimalNumberWithString:#"100" locale:[NSLocale currentLocale]]];
NSLog(#"%#",dec);
instead of Double
I'm not quite sure what to call it, but I have a text field to hold a currency value, so I'm storing that as a NSDecimalNumber. I don't want to use the numbers & symbols keyboard so I'm using a number pad, and inferring the location of a decimal place like ATMs do. It works fine for entering numbers. Type 1234 and it displays $12.34 but now I need to implement back space. So assuming $12.34 is entered hitting back space would show $1.23. I'm not quite sure how to do this with a decimal number. With an int you would just divide by 10 to remove the right most digit, but that obviously doesn't work here. I could do it by some messy converting to int / 10 then back to decimal but that just sounds horrific... Any suggestions?
Call - (NSDecimalNumber *)decimalNumberByDividingBy:(NSDecimalNumber *)decimalNumber withBehavior:(id < NSDecimalNumberBehaviors >)behavior on it
How about using stringValue?
1) NSDecimalNumber to String
2) substring last
3) String to NSDecimalNumber
Below is an example for Swift 3
func popLastNumber(of number: NSDecimalNumber) -> NSDecimalNumber {
let stringFromNumber = number.stringValue //NSNumber property
let lastIndex = stringFromNumber.endIndex
let targetIndex = stringFromNumber.index(before: lastIndex)
let removed = stringFromNumber.substring(to: targetIndex)
return NSDecimalNumber(string: removed)
}
If your input number is a single digit, it would return NaN.
You could replace it to NSDecimalNumber.zero if you need.
It may works like delete button on calcultor.
It's not tested much.
If someone found another NaN case, please report by reply.