问题
OK team, this is weird. [NSDecimalNumber integerValue] is behaving strangely.
I'm sat at a breakpoint, trying to figure out why some parts of my app are broken in iOS8, and I'm looking at a variable called "timeSeconds". It appears like this in the Xcode Variables View:
_timeSeconds (NSDecimalNumber *) 344.514533996581994496
But when I query it in the debugger, I see this:
(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
See that "-5"? Can any of you beautiful people reproduce or explain that, before I file a radar?
Here's the 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
Thanks in advance!
Matthew
回答1:
The result for integerValue is surprising, but from what I understand as documented:
NSDecimalNumber inherits from NSNumber. In the subclassing notes from NSNumber it is stated that "... subclass must override the accessor method that corresponds to the declared type—for example, if your implementation of objCType returns “i”, you must override intValue ..."
objCType is set to the inner pointer, so it should be the same as for NSNumber.
NSDecimal does not override intergerValue. It does override doubleValue, so that should work fine.
The only thing that makes me wonder: it seems it does not override intValue either ...
回答2:
What a great bug! I just found myself tricked by it. So, just to conclude dogsgods answer:
Do NOT:
NSInteger x = [myDecimalNumber integerValue];
Instead, do THIS:
NSInteger x = (NSInteger)[myDecimalNumber doubleValue];
回答3:
Use myDecimalNumber.intValue
instead of integerValue
回答4:
You can use use intValue or unsignedIntValue just fine, but NOT integerValue or unsignedIntegerValue. Here is a unit test that demonstrates the issue, showing that it's related to numbers requiring more than 64 bits of precision:
//
// 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
来源:https://stackoverflow.com/questions/25705511/nsdecimalnumber-integervalue-behaving-strangely-in-ios8