NSDecimalNumber 到 UInt64 的转换问题
Posted
技术标签:
【中文标题】NSDecimalNumber 到 UInt64 的转换问题【英文标题】:NSDecimalNumber to UInt64 conversion issue 【发布时间】:2018-09-20 16:56:50 【问题描述】:我已经实现了以下功能:
func number(for cof: UInt8, limit: UInt64) -> UInt64
let decimalResult = (1 + Decimal(cof)/255) * Decimal(limit) / 1000
return NSDecimalNumber(decimal: decimalResult).uint64Value
使用此函数后与 0 cof
:
number(for: 0, gasLimit: 21000) // Result is 21
但是当我用不同的cof
值调用函数时。
number(for: 128, gasLimit: 21000) // Result is 0
调试 128 cof
值。
我发现了
let decimalResult = (1 + Decimal(gasPriceCoef)/255) * Decimal(gasLimit) / 1000 // Result is 31.5411764705882352941176470588235294116
问题是当我将 Decimal 值转换为 UInt64 时,我收到 0
【问题讨论】:
我认为有一些关于 Swift 中的 Decimal 和/或 NSDecimalNumber 的错误报告,我会看看是否可以找到参考。 证明存在问题的简短示例:NSDecimalNumber(string: "31.54117647058823529").uint64Value
返回 31,但 NSDecimalNumber(string: "31.541176470588235294").uint64Value
返回 13 (!),NSDecimalNumber(string: "31.5411764705882352941").uint64Value
返回 0。
见bugs.swift.org/browse/SR-2980 - 它被标记为“已解决”,但我仍然可以用 Xcode 10 / Swift 4.2 重现该问题
【参考方案1】:
uint64Value
是NSNumber
的一个属性,应用到NSDecimalNumber
时没有很好的记录。
但据我测试,当尾数部分超出UInt64
的范围时,它返回0
。例如31.5411764705882352941176470588235294116
在NSDecimalNumber
中表示为315411764705882352941176470588235294116×10^(-38),而315411764705882352941176470588235294116
大于UInt64.max
。 (*1)
为避免这种行为 (*2),您可以在将十进制值转换为 UInt64
之前对其进行四舍五入。
func number(for cof: UInt8, limit: UInt64) -> UInt64
var decimalResult = (1 + Decimal(cof)/255) * Decimal(limit) / 1000
var roundedResult = Decimal()
NSDecimalRound(&roundedResult, &decimalResult, 0, NSDecimalNumber.RoundingMode.plain)
return NSDecimalNumber(decimal: roundedResult).uint64Value
print(number(for: 128, limit: 21000)) //->32
(*1) 实际行为似乎有点复杂,请参阅上面 Martin R 评论中的短示例。
(*2) 这种行为绝对是一个错误,一旦标记为已解决。另见上述 Martin R 的另一条评论。
【讨论】:
这可能会“解释”为什么NSDecimalNumber(string: "31.541176470588235294").uint64Value
返回 13 ... (31541176470588235294 % 2^64 = 13094432396878683678)。 – 我想知道为什么这个错误(见我上面的评论)被标记为已解决。
@MartinR,错误报告中描述的行为肯定是在 Xcode 10 中找到的。这可能是某种回归,但我不确定为什么会发生这样的事情。无论如何,我们可能需要再次编写相同的错误报告。【参考方案2】:
这是因为 Decimal 值不是整数,& unint64Value 无法处理。你总是可以只使用原语 -
let result = (1 + Double(cof) / 255) * Double(limit) / 1000
return UInt64(result)
这会在操场上返回正确的结果 (31)
【讨论】:
以上是关于NSDecimalNumber 到 UInt64 的转换问题的主要内容,如果未能解决你的问题,请参考以下文章
NSDecimalNumber 返回错误的 uint64_t 值
NSDecimalNumber 和大的无符号长长(64 位)整数
Cardinal 到 OleVariant 的错误转换。 UInt64 没问题
从 uint8_t* 到 uint32_t 的无效转换 - 从 32 位架构迁移到 64 位架构时?