- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我偶然发现了一个奇怪的 NSDecimalNumber 行为:对于某些值,调用 integerValue
、longValue
、longLongValue
等,返回意想不到的值(value)。示例:
let v = NSDecimalNumber(string: "9.821426272392280061")
v // evaluates to 9.821426272392278
v.intValue // evaluates to 9
v.integerValue // evaluates to -8
v.longValue // evaluates to -8
v.longLongValue // evaluates to -8
let v2 = NSDecimalNumber(string: "9.821426272392280060")
v2 // evaluates to 9.821426272392278
v2.intValue // evaluates to 9
v2.integerValue // evaluates to 9
v2.longValue // evaluates to 9
v2.longLongValue // evaluates to 9
这是使用 XCode 7.3;我没有使用早期版本的框架进行测试。
我看过很多关于 NSDecimalNumber
的意外舍入行为的讨论,以及不要使用继承的 NSNumber
初始化器初始化它的警告,但我没有没有看到有关此特定行为的任何信息。不过, 有一些关于内部表示和舍入的相当详细的讨论,其中可能包含我要寻找的金 block ,所以如果我错过了,请提前致歉。
编辑:它隐藏在评论中,但我已将其作为问题 #25465729 提交给 Apple。打开雷达:http://www.openradar.me/radar?id=5007005597040640 .
编辑 2:Apple 已将其标记为 #19812966 的复制品。
最佳答案
由于您已经知道问题是由于“精度太高”造成的,因此您可以先将小数四舍五入来解决此问题:
let b = NSDecimalNumber(string: "9.999999999999999999")
print(b, "->", b.int64Value)
// 9.999999999999999999 -> -8
let truncateBehavior = NSDecimalNumberHandler(roundingMode: .down,
scale: 0,
raiseOnExactness: true,
raiseOnOverflow: true,
raiseOnUnderflow: true,
raiseOnDivideByZero: true)
let c = b.rounding(accordingToBehavior: truncateBehavior)
print(c, "->", c.int64Value)
// 9 -> 9
如果要使用 int64Value
(即 -longLongValue
),请避免使用精度超过 62 位的数字,即避免总共超过 18 位数字。原因解释如下。
NSDecimalNumber 在内部表示为 Decimal structure :
typedef struct {
signed int _exponent:8;
unsigned int _length:4;
unsigned int _isNegative:1;
unsigned int _isCompact:1;
unsigned int _reserved:18;
unsigned short _mantissa[NSDecimalMaxSize]; // NSDecimalMaxSize = 8
} NSDecimal;
这可以使用.decimalValue
获得,例如
let v2 = NSDecimalNumber(string: "9.821426272392280061")
let d = v2.decimalValue
print(d._exponent, d._mantissa, d._length)
// -18 (30717, 39329, 46888, 34892, 0, 0, 0, 0) 4
这意味着 9.821426272392280061 在内部存储为 9821426272392280061 × 10-18 — 请注意 9821426272392280061 = 34892 × 655363 + 46888 × 655362 + 39329 × 65536 + 30717.
现在与 9.821426272392280060 比较:
let v2 = NSDecimalNumber(string: "9.821426272392280060")
let d = v2.decimalValue
print(d._exponent, d._mantissa, d._length)
// -17 (62054, 3932, 17796, 3489, 0, 0, 0, 0) 4
请注意,指数减少到 -17,这意味着 Foundation 省略了尾随零。
知道了内部结构,我现在声明:这个bug是因为34892 ≥ 32768。观察:
let a = NSDecimalNumber(decimal: Decimal(
_exponent: -18, _length: 4, _isNegative: 0, _isCompact: 1, _reserved: 0,
_mantissa: (65535, 65535, 65535, 32767, 0, 0, 0, 0)))
let b = NSDecimalNumber(decimal: Decimal(
_exponent: -18, _length: 4, _isNegative: 0, _isCompact: 1, _reserved: 0,
_mantissa: (0, 0, 0, 32768, 0, 0, 0, 0)))
print(a, "->", a.int64Value)
print(b, "->", b.int64Value)
// 9.223372036854775807 -> 9
// 9.223372036854775808 -> -9
请注意,32768 × 655363 = 263 的值刚好足以溢出一个带符号的 64 位数字。因此,我怀疑该错误是由于 Foundation 将 int64Value
实现为 (1) 将尾数直接转换为 Int64
,然后 (2) 除以 10 |指数|。
事实上,如果你反汇编Foundation.framework,你会发现它基本上就是int64Value
的实现方式(这与平台的指针宽度无关)。
但为什么 int32Value
不受影响?因为它在内部只是实现为 Int32(self.doubleValue)
,所以不会发生溢出问题。不幸的是,double 只有 53 位精度,因此 Apple 别无选择,只能在没有浮点运算的情况下实现 int64Value
(需要 64 位精度)。
关于ios - 正 NSDecimalNumber 返回意外的 64 位整数值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36322336/
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许在 Stack Overflow 上提出有关通用计算硬件和软件的问题。您可以编辑问题,使其成为
当我尝试在 db2 中创建表时,它抛出以下错误 $ db2 CREATE TABLE employee(emp_id INT NOT NULL, emp_name VARCHAR(100)) sh:
我有: while (i < l) { if (one === two) { continue; } i++; } 但是 JSLint 说: Problem at line 1 chara
所以我有这个代码: char inputs[10] = ""; int numInputs = 0; while (numInputs < 10){ char c; printf("E
var ninja = { name: 'Ninja', say: function () { return 'I am a ' + this.name; }
我收到一个我不明白的错误,请注意,我是编码新手,所以这可能是一个简单的错误。 #include using namespace std; int main() { //Initialise Fahr
我正在使用 javascript 和 react,由于某种原因,我收到了一个奇怪的 token 错误。 这是发生错误的代码: renderNavBar() { if (!this.us
Closed. This question is off-topic。它当前不接受答案。
由于某种我无法解释的原因,编译器正在输出一个错误,指出它发现了一个意外的#else 标记。 这发生在文件的开头: #if defined( _USING_MFC ) #include "stda
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况有关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
这个问题在这里已经有了答案: Difference between sh and Bash (11 个答案) 关闭 2 年前。 我正在编写一个简单的 bash 脚本,我在 XX `(' unexpe
关闭。这个问题是not reproducible or was caused by typos .它目前不接受答案。 此问题是由拼写错误或无法再重现的问题引起的。虽然类似的问题可能是 on-topic
我在 Windows 7 上编写了一个脚本,它不断给我一个错误“(此时出乎意料。”对于以下代码 if %vardns%=="NODNS" ( netsh interface ipv4 set ad
我正在尝试使用xmlstarlet(使用xpath)解析XML文件,但是出现语法错误,并且我不知道如何更正我的代码。 这是我的脚本: #!/bin/bash if [ $1=="author" ];
以下脚本旨在在目录中的所有文件上运行程序“senna”,并将每个文件的输出(保留输入文件名)写入另一个目录 for file in ./Data/in/*; do ./senna -iobta
我从 challengers.coffee 运行此代码,并收到错误 ActionView::Template::Error (SyntaxError: [stdin]:3:31:unexpected
我在 config.db.database; 行中有语法错误(意外的标记“.”)。这是我在文件中的代码 const config = require('../config/config') const
这一定很明显,但是我无法使它正常工作。我正在尝试传输应该用于构建$ classKey的对象,这反过来又导致删除所需的软件(amd64或i386)。好吧,这里的代码: $name = @("softwa
我正在使用 1.3.7 版学习 Grails,但我一直无缘无故地遇到以下语法错误: unexpected token: mapping @ line x, column y. 有一次,我通过运行“gr
我正在尝试找出这段Pascal代码的问题 function Factorial(n: integer): integer; begin if n = 0 then Result := 1
我是一名优秀的程序员,十分优秀!