【问题标题】:Getting the decimal part of a double in Swift在 Swift 中获取双精度的小数部分
【发布时间】:2015-10-02 11:32:23
【问题描述】:

我试图在 swift 中分离 double 的小数部分和整数部分。我尝试了多种方法,但都遇到了同样的问题......

let x:Double = 1234.5678
let n1:Double = x % 1.0           // n1 = 0.567800000000034
let n2:Double = x - 1234.0        // same result
let n3:Double = modf(x, &integer) // same result

有没有办法在不将数字转换为字符串的情况下获得 0.5678 而不是 0.567800000000034?

【问题讨论】:

标签: swift math floating-accuracy


【解决方案1】:
extension Double {

    /// Gets the decimal value from a double.
    var decimal: Double {
        Double("0." + string.split(separator: ".").last.string) ?? 0.0
    }
    
    var string: String {
        String(self)
    }
}

这似乎解决了双精度问题。

用法:

print(34.46979988898988.decimal) // outputs 0.46979988898988
print(34.46.decimal) // outputs 0.46

【讨论】:

    【解决方案2】:

    与作为 FloatingPoint 协议的实例属性实现的 Alessandro Ornano 相同的方法:

    Xcode 11 • Swift 5.1

    import Foundation
    
    extension FloatingPoint {
        var whole: Self { modf(self).0 }
        var fraction: Self { modf(self).1 }
    }
    

    1.2.whole    // 1
    1.2.fraction // 0.2
    

    如果您需要小数位数并保留其精度数字,则需要使用 Swift Decimal 类型并使用 String 对其进行初始化:

    extension Decimal {
        func rounded(_ roundingMode: NSDecimalNumber.RoundingMode = .bankers) -> Decimal {
            var result = Decimal()
            var number = self
            NSDecimalRound(&result, &number, 0, roundingMode)
            return result
        }
        var whole: Decimal { self < 0 ? rounded(.up) : rounded(.down) }
        var fraction: Decimal { self - whole }
    }
    

    let decimal = Decimal(string: "1234.99999999")!  // 1234.99999999
    let fractional = decimal.fraction                // 0.99999999
    let whole = decimal.whole                        // 1234
    let sum = whole + fractional                     // 1234.99999999
    

    【讨论】:

    • 它不适用于1234.567。我相应地改变了我的答案。
    • 有什么方法可以将它转换回双精度数?
    • @ScottyBlades (decimal as NSDecimalNumber).doubleValue
    【解决方案3】:

    不可能创建一个适用于所有Doubles 的解决方案。如果其他答案曾经奏效(我也认为这是不可能的),那么它们就不再奏效了。

    let _5678 = 1234.5678.description.drop { $0 != "." } .description // ".5678"
    Double(_5678)  // 0.5678
    
    let _567 = 1234.567.description.drop { $0 != "." } .description // ".567"
    Double(_567) // 0.5669999999999999
    

    【讨论】:

      【解决方案4】:

      斯威夫特 5.1

      let x:Double = 1234.5678
      
      let decimalPart:Double = x.truncatingRemainder(dividingBy: 1)    //0.5678
      let integerPart:Double = x.rounded(.towardZero)                   //1234
      

      这两种方法都返回 Double 值。

      如果你想要一个整数作为整数部分,你可以使用

      Int(x)
      

      【讨论】:

        【解决方案5】:

        C 的数学库中有一个函数,包括 Swift 在内的许多编程语言都可以访问它。它叫做 modf,在 Swift 中是这样工作的

        // modf 返回一个 2 元素元组,

        //第一个元素是整数部分,

        //和第二个元素中的小数部分

        让 splitPi = modf(3.141592)

        splitPi.0 // 3.0

        splitPi.1 // 0.141592

        你可以像下面这样创建一个扩展,

        extension Double {
        
            func getWholeNumber() -> Double {
        
                return modf(self).0
        
            }
        
            func getFractionNumber() -> Double {
        
                return modf(self).1
        
            }
        
        }
        

        【讨论】:

          【解决方案6】:

          你可以像这样得到整数部分:

          let d: Double = 1.23456e12
          
          let intparttruncated = trunc(d)
          let intpartroundlower = Int(d)
          

          trunc() 函数截断小数点后的部分,Int() 函数舍入到下一个较小的值。这对于正数是相同的,但对于负数是不同的。如果你从 d 中减去截断部分,那么你将得到小数部分。

          func frac (_ v: Double) -> Double
          {
              return (v - trunc(v))
          }
          

          您可以像这样获得 Double 值的尾数和指数:

          let d: Double = 1.23456e78
          
          let exponent = trunc(log(d) / log(10.0))
          
          let mantissa = d / pow(10, trunc(log(d) / log(10.0)))
          

          您的结果将是指数 78 和尾数 1.23456。

          希望对你有所帮助。

          【讨论】:

            【解决方案7】:

            斯威夫特 2

            你可以使用:

            modf(x).1
            

            x % floor(abs(x))
            

            【讨论】:

            • 这也会产生与 truncatingRemainder 相同的精度问题。例如,print(modf(35.46).1) 输出 0.46000000000000085 可能会破坏相等和可比较的方法,有时会翻转符号,并且在某些极端情况下除以零,具体取决于您的逻辑。
            【解决方案8】:

            您可以使用truncatingRemainder1 作为分隔符。

            使用截断除法返回此值除以给定值的余数。

            Apple doc

            例子:

            let myDouble1: Double = 12.25
            let myDouble2: Double = 12.5
            let myDouble3: Double = 12.75
            
            let remainder1 = myDouble1.truncatingRemainder(dividingBy: 1)
            let remainder2 = myDouble2.truncatingRemainder(dividingBy: 1)
            let remainder3 = myDouble3.truncatingRemainder(dividingBy: 1)
            
            remainder1 -> 0.25
            remainder2 -> 0.5
            remainder3 -> 0.75
            

            【讨论】:

            • 简单而精确
            • let d: Double = 12.2 let rem = d.truncatingRemainder(dividingBy: 1) //0.1999999999999993
            • @protspace 0.19999999999999930.20 相同。您只需要在向用户显示 NumberFormatter 时使用它。如果您需要更高的精度,可以使用 Decimal 并使用字符串初始化程序。 stackoverflow.com/questions/588004/…
            • @LeoDabus 谢谢,但是如果我在逻辑的后面需要这个 0.2 怎么办?你是怎么处理的?
            • @protspace 抱歉回复晚了。再次如果您需要更高的精度,可以使用Decimal
            【解决方案9】:

            使用Float,因为它的数字精度低于Double

            let x:Double = 1234.5678
            let n1:Float = Float(x % 1)           // n1 = 0.5678
            

            【讨论】:

            • 对于投反对票的人,您能否说明您认为这是错误答案的任何原因?
            • 通过转换为Float,您只是在无益地抛出更多信息:这将给出一个更少准确的答案(0.567799985408782958984375 而不是0.567800000000033833202905952930450439453125 )。
            • @MarkDickinson 这不是 OP 想要完成的吗?
            • 什么,比0.5678 还要进一步的值?我对此表示怀疑。
            【解决方案10】:

            无需将其转换为字符串,您可以像这样向上取整到小数位:

            let x:Double = 1234.5678
            let numberOfPlaces:Double = 4.0
            let powerOfTen:Double = pow(10.0, numberOfPlaces)
            let targetedDecimalPlaces:Double = round((x % 1.0) * powerOfTen) / powerOfTen
            

            你的输出是

            0.5678

            【讨论】:

            • 感谢您的回答。这很好,但不幸的是它对我不起作用,因为我事先不知道代码中适当的位置数量。猜猜我必须通过字符串进行转换:-/
            猜你喜欢
            • 1970-01-01
            • 2018-01-16
            • 2013-06-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-05-05
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多