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
The code for specific digits after decimals is:
let number = 17.9384693864596069567;
let merichle = Float(String(format: "%.1f", (number * 10000)).dropLast(2))!/10000
//merichle = 17.9384
And ultimately, your number gets truncate without round...
You can keep it simple with:
String(format: "%.0f", ratio*100)
Where 0 is the amount of decimals you want to allow. In this case zero. Ratio is a double like: 0.5556633. Hope it helps.
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")
}
I figured this one out.
Just floor (round down) the number, with some fancy tricks.
let x = 1.23556789
let y = Double(floor(10000*x)/10000) // leaves on first four decimal places
let z = Double(floor(1000*x)/1000) // leaves on first three decimal places
print(y) // 1.2355
print(z) // 1.235
So, multiply by 1 and the number of 0s being the decimal places you want, floor that, and divide it by what you multiplied it by. And voila.
Copy this code into your application...
import Foundation
func truncateDigitsAfterDecimal(number: Double, afterDecimalDigits: Int) -> Double {
if afterDecimalDigits < 1 || afterDecimalDigits > 512 {return 0.0}
return Double(String(format: "%.\(afterDecimalDigits)f", number))!
}
Then you can call this function like:
truncateDigitsAfterDecimal(number: 45.123456789, afterDecimalDigits: 3)
Will produce the following:
45.123
In swift 5 truncation to decimal can also be done by creating extension to decimal
extension Decimal {
func truncation(by digit: Int) -> Decimal {
var initialDecimal = self
var roundedDecimal = Decimal()
NSDecimalRound(&roundedDecimal, &initialDecimal, digit, .plain)
return roundedDecimal
}
use case for decimal value
value = Decimal(2.56430).truncation(by:2)
value = 2.560000 (after truncation)