iOS Swift - Convert double to String precision last value rounded to next value - swift

Example value : 3.00035358. i am trying to convert double value to string
Method 1:
let num = NSNumber(value:self)
let formatter : NumberFormatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = 4
let str = formatter.string(from: num)!
return str
method2 :
extension Double {
var stringWithoutZeroFraction: String {
return truncatingRemainder(dividingBy: 1) == 0 ? String(d: "%.0f", self) : String(format:"%.4f", self)
}
}
expecting output to be 3.003 but getting like 3.004. i do want my last digit to be rounded to next digit.how to fix tho issue.any help will be appricated.thanks in advance

If you use a NumberFormatter, you need to set the roundingMode to .floor to achieve truncating. Also, if you want to truncate, you probably want to set maximumFractionDigits instead of minimumFractionDigits.
extension Double {
var string: String {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 4
formatter.roundingMode = .floor
return formatter.string(for: self) ?? description
}
}
3.00035358.string // "3.0003"

Related

How to convert number to words in swift by using func

Given the integer 'number' in the range of 0 ..<< 1000, print the number as a word.
For example, given: let number : Int = 125
output should be one-hundred and twenty-five
You can use NumberFormatter pretty effectively :) Here's example
let numberFormatter = NumberFormatter()
let number = 12355532
numberFormatter.numberStyle = .spellOut
let numberAsWord = numberFormatter.string(from: NSNumber(value: number))
print(numberAsWord)
You could also extend NSNumber to do this behind the scenes like this
public extension NSNumber {
var spelledOut: String? {
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
return formatter.string(from: self)
}
}
To avoid creating a Number Formatter every time you call this property you can create a static formatter. You can also make the computed property generic to support all numeric types:
extension NumberFormatter {
static let spelled: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
return formatter
}()
}
extension Numeric {
var spelledOut: String? { NumberFormatter.spelled.string(for: self) }
}
let integer = 1234
let integerSpelled = integer.spelledOut // "one thousand two hundred thirty-four"
let double = 123.4
let doubleSpelled = double.spelledOut // "one hundred twenty-three point four"

In Swift 5, How to convert a Float to a String localized in order to display it in a textField?

I need to convert a Float to a localized String.
i write this function which is an extension from Float:
func afficherUnFloat() -> String {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.locale = Locale.current
//numberFormatter.maximumFractionDigits = 2
//numberFormatter.maximumIntegerDigits = 6
if let result = numberFormatter.number(from: self) {
return numberFormatter.string(for: result) ?? "0"
}
return "0"
}
but it didn't work:
Here is the exemple
let xxx : Float = 111.222
myTextField.text = String(xxx).afficherUnFloat()
I have installed a pod KSNumericTextField, that limit the numbers in the textfield. He display it only if it is locally formatted.
When i run the app, it doesn't diplay 111,222 in a french region, or 111,222 in an arabic one.
nothing is dislpayed
Note that there is no need to cast your Float to NSNumber. You can use Formatter's method string(for: Any) instead of NumberFormatter's method string(from: NSNumber). Btw it will create a new number formatter every time you call this property. I would make your formatter static:
extension Formatter {
static let decimal: NumberFormatter = {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.locale = .current
numberFormatter.maximumFractionDigits = 2 // your choice
numberFormatter.maximumIntegerDigits = 6 // your choice
return numberFormatter
}()
}
extension FloatingPoint {
var afficherUnFloat: String { Formatter.decimal.string(for: self) ?? "" }
}
let float: Float = 111.222
let string = float.afficherUnFloat // "111.22"
Here is finaly a solution:
extension Float {
func afficherUnFloat() -> String {
let text : NSNumber = self as NSNumber
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.locale = .current
numberFormatter.groupingSeparator = ""
numberFormatter.maximumFractionDigits = 2 // your choice
numberFormatter.maximumIntegerDigits = 6 // your choice
let result = numberFormatter.string(from: text) ?? ""
return result
}
}
With this, you can format every Float to a localized String, compatible with the keyboard choosen by the user, regardless of his locality or langage.
There is no need to force a special keyboard to have a specific decimal separator.
you can use it like this:
let myFloat: Float = 111.222
let myString :String = myFloat.afficherUnFloat()
myString will be displayed as the location requires

Swift String: append 0s after decimal separator

I need to append 0, 1, or 2 0s to a string, depends on its decimal separator, so that
"100", "100." and "100.0" becomes "100.00"
"100.8" becomes "100.80"
"100.85" remains unchanged
I could find the decimal separator and check its distance to end endIndex of the string, but is there an easier way of doing it?
NumberFormatter does this, but the actual string I have, isn't a plain number that can go through a formatter.
For example:
let amount = "123,456,789"
then formatted amount should be "123,456,789.00"
assumption:
the given string has at most one decimal separator with at most two decimal places
So there can't be string like: "123.4.4.5"
Also I want to use the decimal separator from NumberFormatter().decimalSeparator
You could pass the string through a decimal formatter to get the underlying number, and then back again through the formatter to get a formatted string:
let amount = "123,456,789"
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
let number = formatter.number(from: amount)
let newAmountString = formatter.string(from: number!) //"123,456,789.00"
(You should check that number is not nil before force unwrapping it, with if letor guard)
You could wrap this in a function:
func zeroPadding(toString: String) -> String? {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
guard let number = formatter.number(from: toString) else {
return nil
}
return formatter.string(from: number)
}
Here are some test cases:
zeroPadding(toString: "123,456,789") //"123,456,789.00"
zeroPadding(toString: "123,456,789.0") //"123,456,789.00"
zeroPadding(toString: "123,456,789.10") //"123,456,789.10"
zeroPadding(toString: "123,456,789.123") //"123,456,789.12"
zeroPadding(toString: "123.4567") //"123.46"
zeroPadding(toString: "Price: 1€ for a 💩") //nil
Or define it as an extension on String:
extension String {
func withZeroPadding() -> String? {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
guard let number = formatter.number(from: self) else {
return nil
}
return formatter.string(from: number)
}
}
And use it like this:
"123.4.4.5".withZeroPadding() //nil
"12.".withZeroPadding() //"12.00"
"123,456,789".withZeroPadding() //"123,456,789.00"
This is the following code snippet I have tested on Playground, it can be achieved more smartly but for now it is working.
//let amount = "123,456,789.545222323"
//let amount = "123,456,789."
let amount = "123,456,789"
let removeSpaces = amount.replacingOccurrences(of: " ", with: "")
if removeSpaces.count > 0
{
let arrSTR = removeSpaces.components(separatedBy: ".")
if arrSTR.count > 1
{
var strAfterDecimal = arrSTR[1]
if strAfterDecimal.count >= 2
{
strAfterDecimal = strAfterDecimal[0..<2]
}else if strAfterDecimal.count != 0
{
strAfterDecimal = "\(strAfterDecimal)0"
}else
{
strAfterDecimal = "00"
}
let finalSTR = String("\(arrSTR[0]).\(strAfterDecimal)")
print("Final with Decimal - \(finalSTR)")
}else
{
let finalSTR = String(arrSTR[0] + ".00")
print("Final without Decimal - \(finalSTR)")
}
}
extension String {
subscript(_ range: CountableRange<Int>) -> String {
let idx1 = index(startIndex, offsetBy: max(0, range.lowerBound))
let idx2 = index(startIndex, offsetBy: min(self.count, range.upperBound))
return String(self[idx1..<idx2])
}
}

Change scientific notation to decimal in swift

I want to convert a float value from 1e-05 to 0.00001 in Swift.
I used the following extension:
extension Float {
var avoidNotation: String {
let numberFormatter = NumberFormatter()
numberFormatter.allowsFloats = true
numberFormatter.maximumFractionDigits = 8
numberFormatter.numberStyle = .decimal
return numberFormatter.number(from: "\(self)")!.stringValue
}
}
But when I try to using with float value like 1e-05.avoidNotation result its the same, insted of 0.00001
You should use Formatter's string(for:) method instead of getting the stringValue from the number resulting from your string interpolation:
extension Float {
var avoidNotation: String {
let numberFormatter = NumberFormatter()
numberFormatter.maximumFractionDigits = 8
numberFormatter.numberStyle = .decimal
return numberFormatter.string(for: self) ?? ""
}
}
Float(1e-05).avoidNotation // "0.00001"
I would recommend also instead of creating a NumberFormatter every time you call that property extending Formatter and adding a static formatter. You can also extend FloatingPoint instead of Float so you extend all Floating Point types:
extension Formatter {
static let avoidNotation: NumberFormatter = {
let numberFormatter = NumberFormatter()
numberFormatter.maximumFractionDigits = 8
numberFormatter.numberStyle = .decimal
return numberFormatter
}()
}
extension FloatingPoint {
var avoidNotation: String {
return Formatter.avoidNotation.string(for: self) ?? ""
}
}
Float(1e-05).avoidNotation // "0.00001"
Swift 4.2
Disable scientific notation:
let number = NSNumber(value: floatValue)
print(number.decimalValue)
Extension:
extension Float {
func avoidNotation() -> String {
let number = NSNumber(value: self)
return "\(number.decimalValue)"
}
}

How to separate thousands from a Float value with decimals in Swift

I have the following Float: 1123455432.67899
My desired result is a String: 1,123,455,432.67899
Best case correct , and . based on location (US/Europe)
struct Number {
static let withSeparator: NumberFormatter = {
let formatter = NumberFormatter()
formatter.groupingSeparator = ","
formatter.numberStyle = .decimal
return formatter
}()
}
let myFloat: Float = 1123455432.67899
let myNumber = NSNumber(value: myFloat)
let formatter = Number.withSeparator
if let result = formatter.string(from: myNumber) {
print(result)
}
This formatter works great as long as my Float has no decimals, als soon as I'm having decimals it starts to "calculate". It calculates up/down based on the 3rd decimal number.
What am I missing? What's the best way to get a String: 1,123,455,432.67899 from a Float with no matter how many decimal numbers? Help is very appreciated.
Edit:
My exact function:
func formatValue(_ value: String ) -> String {
if let double = Double(value) {
let formatter = Number.withSeparator
if let result = formatter.string(from: NSNumber(value: double)) {
return result
} else {
return value
}
} else {
return value
}
}
value is always a number for example 5.5555555555. But in this specific case the result = 5.556.
Use Double instead of Float, the value you are specifying is not well representable in Float:
// [... your current code ...]
let myDouble: Double = 1123455432.67899
let myNumber = NSNumber(value: myDouble)
// [... your current code ...]
1,123,455,432.679
The e+XX notation Float has by default is not just for show, it is there because Float cannot store all digits. See:
let myFloat2: Float = 1123455432.67899
print(myFloat2 == 1123455432) // true
let notRepresentable = Float(exactly:1123455432.67899) // nil
Fundamentally, your issue comes from the floating point imprecision of Float. Using a double precision floating point data type (Double) will alleviate this, to an extent.
Also, you shouldn't hardcode the groupingSeperator, but instead let it be inferred from the current locale (which is the default behaviour).
import Foundation
let numberFormatter: NumberFormatter = {
let nf = NumberFormatter()
nf.numberStyle = .decimal
return nf
}()
let myDouble = 1123455432.67899
let myNumber = NSNumber(value: myDouble)
// It's also directly initializable from a literal
// let myNumber: NSNumber = 1123455432.67899
guard let result = numberFormatter.string(from: myNumber) else { fatalError() }
print(result)
In addition to luk2302's answer, I would suggest to add it as a method to a Double extension, as follows:
extension Double {
func formattedString(_ maximumFractionDigits: Int = 5) -> String? {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.maximumFractionDigits = maximumFractionDigits
return numberFormatter.string(for:self)
}
}
if let formattedString = 1123455432.67899.formattedString() {
print(formattedString) // 1,123,455,432.67899
}
It might improve the ease of getting the desired string.
Thanks for #rmaddy and #LeoDabus for providing useful notes.