How to truncate decimals to x places in Swift

前端 未结 9 1930
Happy的楠姐
Happy的楠姐 2020-12-04 01:26

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

相关标签:
9条回答
  • 2020-12-04 01:57

    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...

    0 讨论(0)
  • 2020-12-04 01:59

    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.

    0 讨论(0)
  • 2020-12-04 02:03

    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")
    }
    
    0 讨论(0)
  • 2020-12-04 02:06

    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.

    0 讨论(0)
  • 2020-12-04 02:09

    Drag/Drop Solution, IOS, Swift 4

    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
    
    0 讨论(0)
  • 2020-12-04 02:10

    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)

    0 讨论(0)
提交回复
热议问题