NSDecimalNumber integerValue 在 iOS8 中表现异常

Posted

技术标签:

【中文标题】NSDecimalNumber integerValue 在 iOS8 中表现异常【英文标题】:NSDecimalNumber integerValue behaving strangely in iOS8NSDecimalNumber integerValue 在 iOS8 中表现异常 【发布时间】:2014-09-06 22:52:34 【问题描述】:

好的团队,这很奇怪。 [NSDecimalNumber integerValue] 行为异常。

我正坐在断点处,试图找出为什么我的应用程序的某些部分在 ios8 中被破坏,我正在查看一个名为“timeSeconds”的变量。它在 Xcode 变量视图中如下所示:

_timeSeconds    (NSDecimalNumber *) 344.514533996581994496

但是当我在调试器中查询它时,我看到了这个:

(lldb) p [self.timeSeconds doubleValue]
(double) $14 = 344.51453399658192
(lldb) p [self.timeSeconds intValue]
(int) $15 = 344
(lldb) p [self.timeSeconds integerValue]
(NSInteger) $16 = -5
(lldb) p (NSInteger)[self.timeSeconds intValue]
(NSInteger) $17 = 344

看到那个“-5”了吗?在我提交雷达之前,你们中的任何一个漂亮的人都可以复制或解释一下吗?

这里是 SSCCE:

NSDecimalNumber *n = [NSDecimalNumber decimalNumberWithString:@"344.514533996581994496"];
NSLog(@"%@", n); // 344.514533996581994496
NSLog(@"%ld", (long)[n intValue]); // 344
NSLog(@"%ld", (long)[n integerValue]); // -5
NSLog(@"%ld", (long)[n unsignedIntegerValue]); // 12

提前致谢!

马修

【问题讨论】:

马修 - 你发现了什么? iOS 8 上的 NSDecimalNumber 在我的实时应用程序中造成了巨大的问题! 【参考方案1】:

integerValue 的结果令人惊讶,但据我所知,记录如下:

NSDecimalNumber 继承自 NSNumber。在 NSNumber 的子类注释中指出 "...子类必须覆盖与声明类型对应的访问器方法——例如,如果您的 objCType 实现返回“i”,则必须覆盖 intValue ..."

objCType 设置为内部指针,因此它应该与 NSNumber 相同。

NSDecimal 不会覆盖 intergerValue。 它确实覆盖了 doubleValue,所以应该可以正常工作。

唯一让我好奇的是:它似乎也没有覆盖 intValue ...

【讨论】:

我认为你绝对是在正确的轨道上。我想更好地理解这一点,如果您确实了解 intValue,请发布您的答案。但我会继续奖励赏金,因为我认为这是正确的方向,我不希望赏金在没有奖励给你的情况下用完。 这是一个相关问题,其中有一个更具体的 NSDecimal 问题可能与此问题有关。 ***.com/questions/26741233/…【参考方案2】:

多么大的错误!我只是发现自己被它欺骗了。所以,总结一下dogsgods的回答:

不要:

NSInteger x = [myDecimalNumber integerValue];

相反,这样做:

NSInteger x = (NSInteger)[myDecimalNumber doubleValue];

【讨论】:

【参考方案3】:

使用myDecimalNumber.intValue 而不是integerValue

【讨论】:

【参考方案4】:

你可以使用 intValue 或 unsignedIntValue 就好了,但是 NOT integerValue 或 unsignedIntegerValue。这是一个演示该问题的单元测试,表明它与需要超过 64 位精度的数字有关:

//
//  NSDecimalNumberBugTests.m
//
//  Created by Lane Roathe on 6/1/17.
//  For Quicken, Inc.
//

#import <XCTest/XCTest.h>

@interface NSDecimalNumberBugTests : XCTestCase

@end

@implementation NSDecimalNumberBugTests

- (void)setUp 
    [super setUp];
    // Put setup code here. This method is called before the invocation of each test method in the class.


- (void)tearDown 
    // Put teardown code here. This method is called after the invocation of each test method in the class.
    [super tearDown];


- (void)testBug 
    // Use XCTAssert and related functions to verify your tests produce the correct results.
    NSDecimalNumber* decimalLength;
    NSUInteger interval;

    // Start with a number that requires 65+ bits

    // This FAILS (interval is zero)
    decimalLength = [NSDecimalNumber decimalNumberWithString:@"1.8446744073709551616"];
    interval = decimalLength.unsignedIntegerValue;
    XCTAssert(interval == 1);

    // This Works, interval is 1
    interval = decimalLength.unsignedIntValue;
    XCTAssert(interval == 1);

    // Now test with a number that fits in 64 bits

    // This WORKS (interval is 1)
    decimalLength = [NSDecimalNumber decimalNumberWithString:@"1.8446744073709551615"];
    interval = decimalLength.unsignedIntegerValue;
    XCTAssert(interval == 1);


@end

【讨论】:

以上是关于NSDecimalNumber integerValue 在 iOS8 中表现异常的主要内容,如果未能解决你的问题,请参考以下文章

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

NSDecimalNumber 比较失败

对 NSDecimalNumber 有点困惑?

在 NSDecimalNumber 的索引处查找数字

NSDecimalNumber 出现 NaN

为啥 NSDecimalNumber.notANumber.intValue 返回 9?