In my swift program, I have a really long decimal number (say 17.9384693864596069567) and I want to truncate the decimal to a few decimal places (so I want the
Answer for Swift 5.2
I have looked a lot of answers and I always had conversion issues when truncating. According to my maths knowledge, by truncating I understand that if I have 3.1239 and I want 3 decimals then I will have 3.123 with no rounding (!= 3.1234).
Maybe, because of the nature of the process, I was always successful with Doubles but always had issues with Floats.
My approach is to create an extension of BinaryFloatingPoint so I can reuse it for Float, CGFLoat, Double ...
The following extension gets a BinaryFloatingPoint and can return the String or the BinaryFloatingPoint values with a numberOfDecimals given and it handles different type of cases:
extension Numeric where Self: BinaryFloatingPoint {
/// Retruns the string value of the BinaryFloatingPoint. The initiaiser
var toString: String {
return String(describing: self)
}
/// Returns the number of decimals. It will be always greater than 0
var numberOfDecimals: Int {
return toString.count - String(Int(self)).count - 1
}
/// Returns a Number with a certain number of decimals
/// - Parameters:
/// - Parameter numberOfDecimals: Number of decimals to return
/// - Returns: BinaryFloatingPoint with number of decimals especified
func with(numberOfDecimals: Int) -> Self {
let stringValue = string(numberOfDecimals: numberOfDecimals)
if self is Double {
return Double(stringValue) as! Self
} else {
return Float(stringValue) as! Self
}
}
/// Returns a string representation with a number of decimals
/// - Parameters:
/// - Parameter numberOfDecimals: Number of decimals to return
/// - Returns: String with number of decimals especified
func string(numberOfDecimals: Int) -> String {
let selfString = toString
let selfStringComponents = selfString.components(separatedBy: ".")
let selfStringIntegerPart = selfStringComponents[0]
let selfStringDecimalPart = selfStringComponents[1]
if numberOfDecimals == 0 {
return selfStringIntegerPart
} else {
if selfStringDecimalPart.count == numberOfDecimals {
return [selfStringIntegerPart,
selfStringDecimalPart].joined(separator: ".")
} else {
if selfStringDecimalPart.count > numberOfDecimals {
return [selfStringIntegerPart,
String(selfStringDecimalPart.prefix(numberOfDecimals))].joined(separator: ".")
} else {
let difference = numberOfDecimals - selfStringDecimalPart.count
let addedCharacters = [Character].init(repeating: "0", count: difference)
return [selfStringIntegerPart,
selfStringDecimalPart+addedCharacters].joined(separator: ".")
}
}
}
}
}
It might look old school but all my tests are passing:
func test_GivenADecimalNumber_ThenAssertNumberOfDecimalsWanted() {
//No decimals
XCTAssertEqual(Float(3).with(numberOfDecimals: 0), 3)
XCTAssertEqual(Float(3.09).with(numberOfDecimals: 0), 3)
XCTAssertEqual(Float(3.999).with(numberOfDecimals: 0), 3)
XCTAssertEqual(Double(3).with(numberOfDecimals: 0), 3)
XCTAssertEqual(Double(3.09).with(numberOfDecimals: 0), 3)
XCTAssertEqual(Double(3.999).with(numberOfDecimals: 0), 3)
//numberOfDecimals == totalNumberOfDecimals
XCTAssertEqual(Float(3.00).with(numberOfDecimals: 2), 3.00)
XCTAssertEqual(Float(3.09).with(numberOfDecimals: 2), 3.09)
XCTAssertEqual(Float(3.01).with(numberOfDecimals: 2), 3.01)
XCTAssertEqual(Float(3.999).with(numberOfDecimals: 3), 3.999)
XCTAssertEqual(Float(3.991).with(numberOfDecimals: 3), 3.991)
XCTAssertEqual(Double(3.00).with(numberOfDecimals: 2), 3.00)
XCTAssertEqual(Double(3.09).with(numberOfDecimals: 2), 3.09)
XCTAssertEqual(Double(3.01).with(numberOfDecimals: 2), 3.01)
XCTAssertEqual(Double(3.999).with(numberOfDecimals: 3), 3.999)
XCTAssertEqual(Double(3.991).with(numberOfDecimals: 3), 3.991)
//numberOfDecimals < totalNumberOfDecimals
XCTAssertEqual(Float(3.00).with(numberOfDecimals: 1), 3.0)
XCTAssertEqual(Float(3.09).with(numberOfDecimals: 1), 3.0)
XCTAssertEqual(Float(3.01).with(numberOfDecimals: 1), 3.0)
XCTAssertEqual(Float(3.999).with(numberOfDecimals: 2), 3.99)
XCTAssertEqual(Float(3.991).with(numberOfDecimals: 2), 3.99)
XCTAssertEqual(Double(3.00).with(numberOfDecimals: 1), 3.0)
XCTAssertEqual(Double(3.09).with(numberOfDecimals: 1), 3.0)
XCTAssertEqual(Double(3.01).with(numberOfDecimals: 1), 3.0)
XCTAssertEqual(Double(3.999).with(numberOfDecimals: 2), 3.99)
XCTAssertEqual(Double(3.991).with(numberOfDecimals: 2), 3.99)
//numberOfDecimals > totalNumberOfDecimals
XCTAssertEqual(Float(3.00).with(numberOfDecimals: 3), 3.000)
XCTAssertEqual(Float(3.09).with(numberOfDecimals: 3), 3.090)
XCTAssertEqual(Float(3.01).with(numberOfDecimals: 3), 3.010)
XCTAssertEqual(Float(3.999).with(numberOfDecimals: 4), 3.9990)
XCTAssertEqual(Float(3.991).with(numberOfDecimals: 4), 3.9910)
XCTAssertEqual(Double(3.00).with(numberOfDecimals: 3), 3.000)
XCTAssertEqual(Double(3.09).with(numberOfDecimals: 3), 3.090)
XCTAssertEqual(Double(3.01).with(numberOfDecimals: 3), 3.010)
XCTAssertEqual(Double(3.999).with(numberOfDecimals: 4), 3.9990)
XCTAssertEqual(Double(3.991).with(numberOfDecimals: 4), 3.9910)
}
func test_GivenADecimal_ThenAssertStringValueWithDecimalsWanted() {
//No decimals
XCTAssertEqual(Float(3).string(numberOfDecimals: 0), "3")
XCTAssertEqual(Float(3.09).string(numberOfDecimals: 0), "3")
XCTAssertEqual(Float(3.999).string(numberOfDecimals: 0), "3")
XCTAssertEqual(Double(3).string(numberOfDecimals: 0), "3")
XCTAssertEqual(Double(3.09).string(numberOfDecimals: 0), "3")
XCTAssertEqual(Double(3.999).string(numberOfDecimals: 0), "3")
//numberOfDecimals == totalNumberOfDecimals
XCTAssertEqual(Float(3.00).string(numberOfDecimals: 2), "3.00")
XCTAssertEqual(Float(3.09).string(numberOfDecimals: 2), "3.09")
XCTAssertEqual(Float(3.01).string(numberOfDecimals: 2), "3.01")
XCTAssertEqual(Float(3.999).string(numberOfDecimals: 3), "3.999")
XCTAssertEqual(Float(3.991).string(numberOfDecimals: 3), "3.991")
XCTAssertEqual(Double(3.00).string(numberOfDecimals: 2), "3.00")
XCTAssertEqual(Double(3.09).string(numberOfDecimals: 2), "3.09")
XCTAssertEqual(Double(3.01).string(numberOfDecimals: 2), "3.01")
XCTAssertEqual(Double(3.999).string(numberOfDecimals: 3), "3.999")
XCTAssertEqual(Double(3.991).string(numberOfDecimals: 3), "3.991")
//numberOfDecimals < totalNumberOfDecimals
XCTAssertEqual(Float(3.00).string(numberOfDecimals: 1), "3.0")
XCTAssertEqual(Float(3.09).string(numberOfDecimals: 1), "3.0")
XCTAssertEqual(Float(3.01).string(numberOfDecimals: 1), "3.0")
XCTAssertEqual(Float(3.999).string(numberOfDecimals: 2), "3.99")
XCTAssertEqual(Float(3.991).string(numberOfDecimals: 2), "3.99")
XCTAssertEqual(Double(3.00).string(numberOfDecimals: 1), "3.0")
XCTAssertEqual(Double(3.09).string(numberOfDecimals: 1), "3.0")
XCTAssertEqual(Double(3.01).string(numberOfDecimals: 1), "3.0")
XCTAssertEqual(Double(3.999).string(numberOfDecimals: 2), "3.99")
XCTAssertEqual(Double(3.991).string(numberOfDecimals: 2), "3.99")
//numberOfDecimals > totalNumberOfDecimals
XCTAssertEqual(Float(3.00).string(numberOfDecimals: 3), "3.000")
XCTAssertEqual(Float(3.09).string(numberOfDecimals: 3), "3.090")
XCTAssertEqual(Float(3.01).string(numberOfDecimals: 3), "3.010")
XCTAssertEqual(Float(3.999).string(numberOfDecimals: 4), "3.9990")
XCTAssertEqual(Float(3.991).string(numberOfDecimals: 4), "3.9910")
XCTAssertEqual(Double(3.00).string(numberOfDecimals: 3), "3.000")
XCTAssertEqual(Double(3.09).string(numberOfDecimals: 3), "3.090")
XCTAssertEqual(Double(3.01).string(numberOfDecimals: 3), "3.010")
XCTAssertEqual(Double(3.999).string(numberOfDecimals: 4), "3.9990")
XCTAssertEqual(Double(3.991).string(numberOfDecimals: 4), "3.9910")
}