如果 NSDecimalNumber 大于 Uint64,如何将 NSDecimalNumber 转换为字节数组?

Posted

技术标签:

【中文标题】如果 NSDecimalNumber 大于 Uint64,如何将 NSDecimalNumber 转换为字节数组?【英文标题】:How to convert NSDecimalNumber to byte array if NSDecimalNumber is bigger than Uint64? 【发布时间】:2018-12-06 23:38:40 【问题描述】:

我想将 NSDecimalNumber 转换为字节数组。另外,我不想使用 BigInt、BInt 等任何库。

我试过这个:

static func toByteArray<T>(_ value: T) -> [UInt8] 
    var value = value
    return withUnsafeBytes(of: &value)  Array($0) 

let value =  NSDecimalNumber(string: "...")
let bytes = toByteArray(value.uint64Value)

如果数字不大于 Uint64,则效果很好。但是,如果是的话,我们如何转换它呢?

【问题讨论】:

问题是什么,“如何将 NSDecimalNumber 转换为字节数组?”或“如果数字大于 Uint64 会发生什么?”? 我刚刚改变了问题。很抱歉造成误导。 你能举一个value和expexted bytes的例子吗? value 总是整数吗? 例如,如果值为“59785897542892656787456”,则该值必须为 [12, 169, 0, 0, 0, 0, 0, 0,0 ,0]。像 java 中的 new BigInteger("59785897542892656787456").toByteArray。 看看value.decimalValue和它的_mantissa 【参考方案1】:

问题显然是uint64Value的使用,它显然不能代表任何大于UInt64.max的值,而你的例子59,785,897,542,892,656,787,456大于那个。

如果要获取 128 位尾数的字节表示,可以使用 UInt16Decimal 的字组的 _mantissa 元组,并根据需要将它们转换为字节。例如

extension Decimal 
    var byteArray: [UInt8] 
        return [_mantissa.0,
                _mantissa.1,
                _mantissa.2,
                _mantissa.3,
                _mantissa.4,
                _mantissa.5,
                _mantissa.6,
                _mantissa.7]
            .flatMap  [UInt8($0 & 0xff), UInt8($0 >> 8)] 
    

还有

if let foo = Decimal(string: "59785897542892656787456") 
    print(foo.byteArray)

返回:

[0, 0, 0, 0, 0, 0, 0, 0, 169, 12, 0, 0, 0, 0, 0, 0]

诚然,这只是推迟了问题,打破了 uint64Value 方法的 64 位限制,但仍受限于 NSDecimalNumber/Decimal 固有的 128 位限制。要捕获大于 128 位的数字,您需要完全不同的表示形式。


注意:这也假设指数是0。但是,如果您有一些较大的数字,例如4.2e101 (4.2 * 10101),指数为100,尾数为42,我敢打赌,这可能不是你想要的字节数组。再说一次,这是一个数字太大而无法表示为单个 128 位整数的示例:

if let foo = Decimal(string: "4.2e101") 
    print(foo.byteArray)
    print(foo.exponent)

产量:

[42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 100

【讨论】:

【参考方案2】:

您传递的是value.uint64Value,而不是value。因此,数字将 环绕 零,以使 NSDecimal 适合 UInt64 格式。

传递"18446744073709551616"(也就是UInt64.max+1对应的字符串)等价于传递"0"。传递"18446744073709551617"(也就是UInt64.max+2对应的字符串)相当于传递"1"

【讨论】:

传递 NSDecimalNumber 不会给出正确的结果。 这回答了您的原始问题,解释了当数字大于 UInt64 时会发生什么。 抱歉误导。 MemoryLayout&lt;NSDecimalNumber&gt;.size 是指针的大小吗? NSDecimalNumber 对象的大小无关紧要。它是一个围绕NSDecimal(Swift 中的Decimal)的面向对象的包装器。 NSDecimalNumberNSDecimal 可以处理字符串 "18446744073709551616""18446744073709551617"。方法uint64Value 无法将这些值转换为UInt64

以上是关于如果 NSDecimalNumber 大于 Uint64,如何将 NSDecimalNumber 转换为字节数组?的主要内容,如果未能解决你的问题,请参考以下文章

核心数据和 iPhone 的 NSDecimalNumber 问题

从 NSDecimalNumber 中删除负号 (-) 给我一个错误

将 NSDecimalNumber 变为负数

为啥来自 NSString 分配的 NSDecimalNumber 会失败?

NSDecimalNumber 比较失败

对 NSDecimalNumber 有点困惑?