Swift 3 中的十进制到双精度转换

Posted

技术标签:

【中文标题】Swift 3 中的十进制到双精度转换【英文标题】:Decimal to Double conversion in Swift 3 【发布时间】:2016-10-06 08:08:48 【问题描述】:

我正在将一个项目从 Swift 2.2 迁移到 Swift 3,并且我正在尝试尽可能摆脱旧的 Cocoa 数据类型。

我的问题在这里:将NSDecimalNumber 迁移到Decimal

我曾经在 Swift 2.2 中双向桥接 NSDecimalNumberDouble

let double = 3.14
let decimalNumber = NSDecimalNumber(value: double)

let doubleFromDecimal = decimalNumber.doubleValue

现在,切换到 Swift 3

let double = 3.14
let decimal = Decimal(double)

let doubleFromDecimal = ???

decimal.doubleValue 不存在,Double(decimal) 也不存在,甚至 decimal as Double... 我想出的唯一技巧是:

let doubleFromDecimal = (decimal as NSDecimalNumber).doubleValue

但是尝试摆脱 NSDecimalNumber 是完全愚蠢的,并且不得不偶尔使用它...

好吧,在我看来,要么我错过了一些明显的事情,请原谅浪费你的时间,要么我认为有一个漏洞需要解决......

提前感谢您的帮助。

编辑:在 Swift 4 上没有更多关于该主题的内容。

编辑:关于 Swift 5 的主题不再赘述。

【问题讨论】:

是的,在 2018 年仍然很糟糕......我犹豫是否转向 Decimal 的唯一原因是所有的数学运算(包括文字转换,如 myDecimal + 1)和比较比使用 NSDecimalNumber 更直接 【参考方案1】:

NSDecimalNumberDecimal 被桥接

Foundation 框架的 Swift 覆盖提供了 Decimal 结构,它连接到 NSDecimalNumber 类。十进制 值类型提供与 NSDecimalNumber 相同的功能 引用类型,两者在 Swift 代码中可以互换使用 与 Objective-C API 交互。这种行为类似于 Swift 将标准字符串、数字和集合类型连接到它们的 相应的基础类。 Apple Docs

但与其他一些桥接类型一样,缺少某些元素。

要恢复功能,您可以编写一个扩展:

extension Decimal 
    var doubleValue:Double 
        return NSDecimalNumber(decimal:self).doubleValue
    


// implementation
let d = Decimal(floatLiteral: 10.65)
d.doubleValue

【讨论】:

问题还是一样的,即使它被扩展隐藏了。您仍然必须使用第三种类型,而不是只有 'Double` 和 Decimal... 投票赞成这是 Swift 4 中公认的答案。虽然不是理想的解决方案,但 Decimal 类型应该在 Swift 语言中干净利落地提供双重转换。【参考方案2】:

另一个适用于 Swift 3 的解决方案是将 Decimal 转换为 NSNumber 并从中创建 Double

let someDouble = Double(someDecimal as NSNumber)

从 Swift 4.2 开始,您需要:

let someDouble = Double(truncating: someDecimal as NSNumber)

【讨论】:

let c = Double(truncating:a as NSNumber) 在 Swift 4 中 @vikingosegundo 那是 Swift 4。在 Swift 3 中你不需要 truncating 是的,但您仍然必须转换为第三种类型...您不能只使用DoubleDecimal,您需要使用NSDecimalNumberNSNumber。 ..【参考方案3】:

适用于 Swift 4

的解决方案
let double = 3.14
let decimal = Decimal(double)
let doubleFromDecimal = NSDecimalNumber(decimal: decimal).doubleValue
print(doubleFromDecimal)

【讨论】:

是的,但您仍然必须转换为第三种类型...您不能只使用DoubleDecimal,您需要使用NSDecimalNumberNSNumber。 ..这就是这篇文章的重点...... @Zaphod 你检查了吗 确实有效...但是您仍然使用第三种类型,这里使用String 使用description 属性。这篇文章背后的整个想法是代码的效率和清晰度问题。关键是Decimal 旨在替换旧的NSDecimalNumber 类型不能在不使用hack 的情况下透明地使用(通过NSNumberNSDecimalNumberString、指针或其他东西......)这个问题比关于开发更具修辞性......而缺失的方法在 Swift 4 中仍然缺失。 永远不要依赖任何类型的 .description 属性。这在某些时候肯定会出错。 使用description的糟糕答案【参考方案4】:

斯威夫特 5

let doubleValue = Double(truncating: decimalValue as NSNumber)

【讨论】:

确实有效...但是您仍然使用第三种类型,这里是NSNumber。这篇文章背后的整个想法是代码的效率和清晰度问题。关键是Decimal 旨在替换旧的NSDecimalNumber 类型不能在不使用hack 的情况下透明地使用(通过NSNumberNSDecimalNumberString、指针或其他东西......)这个问题比关于开发更具修辞性......而且缺少的方法在 Swift 5 中仍然缺失。【参考方案5】:

Swift 3 中的Decimal 不是NSDecimalNumber。是NSDecimal,完全不同的类型。

您应该像以前一样继续使用NSDecimalNumber

【讨论】:

其实Decimal对应NSDecimalNumber:The Swift overlay to the Foundation framework provides the Decimal structure, which bridges to the NSDecimalNumber class. The Decimal value type offers the same functionality as the NSDecimalNumber reference type, and the two can be used interchangeably in Swift code that interacts with Objective-C APIsdeveloper.apple.com/reference/foundation/nsdecimalnumber @AlexanderMomchliov,除了它没有可用的NSDecimalNumber 方法,所以为了使用它们,你仍然需要将Decimal 转换为NSDecimalNumber。来源:刚刚在操场上检查过。 是的,我没有声称它确实如此。我刚才说你假设Decimal对应NSDecimal是错误的。 @AlexanderMomchliov,但 它是 NSDecimal,只需将 Swift 3 生成的标头中的 Decimal 与 Foundation C 代码中的 NSDecimal 进行比较。仅仅因为 Swift bridges Decimal 旧 Foundation 代码的变量在导入时变为 NSDecimalNumber 并不意味着 Decimal 变为 NSDecimalNumber 尽管文档说 Decimal 桥接到 NSDecimalNumber,但根据我的经验,现在坚持使用 NSDecimalNumber 是最安全的,并等待库稳定(尝试转换为 Decimal,结果更加混乱超出我的预期,加上生成的代码崩溃了)。【参考方案6】:

您应该使用as 运算符将 Swift 类型转换为它的 bridged 底层 Objective-C 类型。所以只需像这样使用as

let p = Decimal(1)
let q = (p as NSDecimalNumber).doubleValue

在 Swift 4 中,Decimal NSDecimalNumber。这是来自 Apple 在 Xcode 10 中的官方文档的引用。

重要

Foundation 框架的 Swift 覆盖提供了Decimal 结构,它连接到NSDecimalNumber 类。更多 有关值类型的信息,请参阅使用 Cocoa 框架 将 Swift 与 Cocoa 和 Objective-C 结合使用(Swift 4.1)。

不再有NSDecimal。 Swift 3 中有令人困惑的 NSDecimal 类型,但这似乎是一个错误。 没有更多的困惑。

注意

我看到 OP 对 Swift 4 不感兴趣,但我添加了这个答案,因为只提到(过时的)Swift 3 让我感到困惑。

【讨论】:

这里的意思不是底层结构,而是语法问题。我知道您已经使用 as 运算符强制转换为桥接类型。我的问题只是“如果您仍然需要偶尔引用它,那么使用 Decimal 而不是 NSDecimalNumber 有什么意义?”,例如要获得双倍值......没有办法改变你的从NSDecimalNumberDecimal 的代码透明地......这正是我的意思......在Swift 4 中仍然是这种情况(如编辑中所述) @Zaphod 重点是“使用正确的值类型”。您应该知道,许多NS- 类实际上被设计为值类型,但由于 OBJC 中缺乏语言结构而在类中实现。 IMO,Apple 希望为 Swift 用户提供更多“Swift-y”库,但他们向 Swift 的过渡还没有完成。 Apple 库中有很多像这样的“不完整”部分,以后可能会更新。 IMO,一旦 Apple 成功迁移到 Swift 后,他们将一一移除对 OBJC 的访问权限。 这正是我的意思...转换并没有完全结束 Decimal... 桥接到一个类型与成为那个类型不同。 String 连接到 NSStringDictionaryNSMutableDictionary,但它们是完全不同的类型。延伸阅读:developer.apple.com/documentation/swift/…【参考方案7】:

对于swift 5,函数是

let doubleValue = Double(truncating: decimalValue as NSNumber)

下面的例子,显示浮点数。

let decimalValue: Decimal = 3.14159
let doubleValue = Double(truncating: decimalValue as NSNumber)
print(String(format: "%.3f", doubleValue))  // 3.142
print(String(format: "%.4f", doubleValue)) // 3.1416
print(String(format: "%.5f", doubleValue)) // 3.14159
print(String(format: "%.6f", doubleValue)) // 3.141590
print(String(format: "%.7f", doubleValue)) // 3.1415900

【讨论】:

确实有效...但是您仍然使用第三种类型,这里是NSNumber。这篇文章背后的整个想法是代码的效率和清晰度问题。关键是Decimal 旨在替换旧的NSDecimalNumber 类型不能在不使用hack 的情况下透明地使用(通过NSNumberNSDecimalNumberString、指针或其他东西......)这个问题比关于开发更具修辞性......而且缺失的方法在 Swift 5 中仍然缺失。【参考方案8】:

在 Swift 开源中,implementation 实际上是在Decimal.swift 中完成的,但它是私有的。您可以从那里重复使用代码。

extension Double 
    @inlinable init(_ other: Decimal) 
        if other._length == 0 
            self.init(other._isNegative == 1 ? Double.nan : 0)
            return
        

        var d: Double = 0.0
        for idx in (0..<min(other._length, 8)).reversed() 
            var m: Double
            switch idx 
            case 0: m = Double(other._mantissa.0)
                break
            case 1: m = Double(other._mantissa.1)
                break
            case 2: m = Double(other._mantissa.2)
                break
            case 3: m = Double(other._mantissa.3)
                break
            case 4: m = Double(other._mantissa.4)
                break
            case 5: m = Double(other._mantissa.5)
                break
            case 6: m = Double(other._mantissa.6)
                break
            case 7: m = Double(other._mantissa.7)
                break
            default: break
            
            d = d * 65536 + m
        

        if other._exponent < 0 
            for _ in other._exponent..<0 
                d /= 10.0
            
         else 
            for _ in 0..<other._exponent 
                d *= 10.0
            
        
        self.init(other._isNegative != 0 ? -d : d)
    

【讨论】:

确实是的,但真正的问题仍然存在,为什么 doubleValue 被声明为 internal 而不是 public,因为它显然应该是......也许必须完成拉取请求...... .但我不知道如何提出这个......?‍♂️ 您可以在 swift 论坛上创建进化提案,您将获得当前实施背后的反馈和推理,以及有关提案的考虑。 是的,我会把它添加到我的待办事项列表中......?

以上是关于Swift 3 中的十进制到双精度转换的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 中将半精度浮点数(字节)转换为浮点数

变体浮点到双精度值转换,舍入到小数点后 1 位 [重复]

如何将单精度浮点数转换为十进制?

在 Swift 中转换十进制、二进制和十六进制

在 C# 中将十进制数转换为双精度数会产生差异

在c#中将十进制值转换为两个精度[重复]