【问题标题】:How to truncate decimals to x places in Swift如何在 Swift 中将小数截断到 x 位
【发布时间】:2016-06-27 01:39:13
【问题描述】:

我有一个非常长的十进制数(比如17.9384693864596069567),我想将小数截断到几个小数位(所以我希望输出为17.9384)。我确实不想将数字四舍五入为17.9385。

我该怎么做?

【问题讨论】:

标签: ios swift double decimal


【解决方案1】:

我想出了这个。

用一些花哨的技巧将数字取整(向下取整)。

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

因此,乘以 1,0 的数量是您想要的小数位数,将其取底,然后除以您乘以的数。瞧。

【讨论】:

    【解决方案2】:

    您可以通过将其扩展为Double 来进一步整理:

    extension Double {
        func truncate(places : Int)-> Double {
            return Double(floor(pow(10.0, Double(places)) * self)/pow(10.0, Double(places)))
        }
    }
    

    你可以这样使用它:

    var num = 1.23456789
    // return the number truncated to 2 places
    print(num.truncate(places: 2))
    
    // return the number truncated to 6 places
    print(num.truncate(places: 6))
    

    【讨论】:

    • 我喜欢!如此有条理,当你使用它时它实际上是有意义的:)。
    • 很高兴你喜欢它 - 这是我写的第一个扩展,所以我们今天都学到了一些东西 :-)
    • 这不适用于小于 1 的数字 :( 如果你使用 0.23456789.truncate(2) 你会得到0.23000000000000001
    • 这个值怎么样:“2315385.01”,它给出了2315385,这是错误的
    • Jeroen Bakker - 我不知道你为什么这么认为,因为它确实返回 0.23 @Samarth Kejriwal - 这很奇怪。对于幻数两侧的数字,它不会这样做
    【解决方案3】:

    小数点后具体数字的代码为:

    let number = 17.9384693864596069567;
    let merichle = Float(String(format: "%.1f", (number * 10000)).dropLast(2))!/10000
    
    //merichle = 17.9384
    

    最终,您的号码会被截断而不用四舍五入...

    【讨论】:

    • 如果您正在寻找一个字符串作为输出而不是一个 Double,这看起来会很好。
    • @owlswipe 如果你想要双数,你可以使用这个代码:Double(String(format: "%.2f", b))
    • 好电话,我觉得这样很好:)
    • @RamazanKarami String(format: "%.2f", b) 将舍入一个双精度,而不是截断它。
    • @George 是的,你是对的。答案已通过没有四舍五入的新编辑得到纠正。感谢您的跟进
    【解决方案4】:

    拖放解决方案,IOS,Swift 4

    将此代码复制到您的应用程序中...

    import Foundation
    
    func truncateDigitsAfterDecimal(number: Double, afterDecimalDigits: Int) -> Double {
       if afterDecimalDigits < 1 || afterDecimalDigits > 512 {return 0.0}
       return Double(String(format: "%.\(afterDecimalDigits)f", number))!
    }
    

    然后你可以像这样调用这个函数:

    truncateDigitsAfterDecimal(number: 45.123456789, afterDecimalDigits: 3)
    

    将产生以下内容:

    45.123
    

    【讨论】:

    • 这将舍入它,而不是截断
    【解决方案5】:
    extension Double {
        /// Rounds the double to decimal places value
        func roundToPlaces(_ places:Int) -> Double {
            let divisor = pow(10.0, Double(places))
            return (self * divisor).rounded() / divisor
        }
        func cutOffDecimalsAfter(_ places:Int) -> Double {
            let divisor = pow(10.0, Double(places))
            return (self*divisor).rounded(.towardZero) / divisor
        }
    }
    
    let a:Double = 1.228923598
    
    print(a.roundToPlaces(2)) // 1.23
    print(a.cutOffDecimalsAfter(2)) // 1.22
    

    【讨论】:

      【解决方案6】:

      Swift 用户界面: 如果您在视图中截断以格式化输出,而不是计算,则 SwiftUI 包含一种使用 C 格式说明符作为 Text() 签名的一部分的便捷方法。

      import SwiftUI
      
      let myDouble = 17.93846938645960695
      Text("\(myDouble, specifier: "%.2f")")
      ///Device display: 17.94
      

      以上代码会将Double的内容直接输出到View,正确舍入到百位,但保留Double的值以供进一步计算。

      如果您作为 SwiftUI 用户不熟悉 C 语言格式说明符,此链接包含有用信息:https://en.wikipedia.org/wiki/Printf_format_string

      【讨论】:

      • 您的解决方案是对十进制数进行四舍五入...再读一遍这个问题。
      【解决方案7】:

      你可以保持简单:

      String(format: "%.0f", ratio*100)
      

      其中 0 是您希望允许的小数位数。在这种情况下为零。比率是双倍的:0.5556633。 希望能帮助到你。

      【讨论】:

      • 如果您想将 double 作为字符串使用,这是正确的答案。
      【解决方案8】:

      在 swift 5 中,也可以通过创建小数的扩展来完成对小数的截断

      extension Decimal {
        func truncation(by digit: Int) -> Decimal {
          var initialDecimal = self
          var roundedDecimal = Decimal()
          NSDecimalRound(&roundedDecimal, &initialDecimal, digit, .plain)
          return roundedDecimal
        }
      

      十进制值的用例

      value = Decimal(2.56430).truncation(by:2)
      

      值 = 2.560000(截断后)

      【讨论】:

      • 我喜欢这背后的想法,但有一个错误。这实际上是四舍五入,它不会截断。 .plain 导致 NSDecimalRound 向上或向下舍入。将其替换为 .down 以始终向下舍入,这将使其具有截断的行为。
      • @stef 这是相对的。如果数字为负数,则必须使用 .up。检查这个post
      【解决方案9】:

      Swift 5.2 的答案

      我查看了很多答案,但在截断时总是遇到转换问题。根据我的数学知识,通过截断,我知道如果我有 3.1239 并且我想要 3 位小数,那么我将有 3.123 没有四舍五入 (!= 3.1234)。

      也许,由于过程的性质,我在 Doubles 上总是很成功,但在 Floats 上总是遇到问题。

      我的方法是创建 BinaryFloatingPoint 的扩展,这样我就可以将它重用于 Float、CGFLoat、Double ...

      以下扩展获取一个 BinaryFloatingPoint 并可以返回带有 numberOfDecimals 的 String 或 BinaryFloatingPoint 值,并处理不同类型的情况:

      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: ".")
                      }
                  }
              }
          }
              
      }
      

      它可能看起来很老套,但我所有的测试都通过了:

      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")
      }
      

      【讨论】:

      • 扩展Numeric 协议并将其限制为BinaryFloatingPoint 是没有意义的
      • Double(stringValue) as! Self ???顺便说一句,如果不是Double,你有什么保证它是Float。试试Float80(123.456789).with(numberOfDecimals: 2) // 繁荣
      • @LeoDabus 你看到逻辑是围绕协议 BinaryFloatingPoint 吗?这个想法是你不在乎它是否是 Float、Double、Float80 ... as! Self 只是为了帮助编译器......另外,试着看看当你在边缘情况下使用浮点数和双精度数时会发生什么
      • 这是错误的。您需要的是使用泛型方法返回泛型类型。如果你想返回 Self 你应该返回调用该方法的相同类型。您没有帮助编译器确认它是浮点数,但不能保证。
      • 顺便说一句,我不赞成将字符串转换为字符串来操作浮点数,但如果要将字符串转换为二进制浮点数,则需要将 Self 限制为 LosslessStringConvertible。请注意,它不会覆盖CGFloat,因为它不符合它。
      【解决方案10】:

      方式一:如果你不想为此创建任何新的函数,你可以这样做,直接获取四舍五入的值。

      var roundedValue = (decimalValue * pow(10.0, Double(numberOfPlaces))).rounded())/pow(10.0, Double(numberOfPlaces)
      

      例子:

      var numberOfPlaces = 2
      var decimalValue = 13312.2423423523523
      
      print("\(((decimalValue * pow(10.0, Double(numberOfPlaces))).rounded())/pow(10.0, Double(numberOfPlaces)))")
      

      结果:1​​3312.24



      方式2:如果你只是想打印,你可以使用:

      print(String(format: "%.\(numberOfPlaces)f",decimalValue))
      

      例子

      var numberOfPlaces = 4
      var decimalValue = 13312.2423423523523
      
      print(String(format: "%.\(numberOfPlaces)f",decimalValue))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-12
        • 1970-01-01
        • 2017-07-04
        • 1970-01-01
        相关资源
        最近更新 更多