{NSDecimalNumber integerValue} ведет себя странно в iOS8

Команда OK, это странно. [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

Спасибо заранее!

Мэтью

Ответ 1

Результат для integerValue является неожиданным, но из того, что я понимаю, как описано:

NSDecimalNumber наследуется от NSNumber. В примечаниях к подклассам из NSNumber указано, что "... подкласс должен переопределить метод доступа, который соответствует объявленному типу - например, если ваша реализация objCType возвращает" я ", вы должны переопределить intValue..."

objCType установлен на внутренний указатель, поэтому он должен быть таким же, как для NSNumber.

NSDecimal не переопределяет intergerValue. Он переопределяет doubleValue, поэтому он должен работать нормально.

Единственное, что заставляет меня задуматься: кажется, что он не переопределяет intValue либо...

Ответ 2

Какая замечательная ошибка! Я просто обманул его. Итак, просто чтобы закончить doggods:

НЕ:

NSInteger x = [myDecimalNumber integerValue];

Вместо этого сделайте ЭТО:

NSInteger x = (NSInteger)[myDecimalNumber doubleValue];

Ответ 3

Используйте myDecimalNumber.intValue вместо integerValue

Ответ 4

Вы можете использовать use intValue или unsignedIntValue просто отлично, но НЕ integerValue или unsignedIntegerValue. Ниже приведен пример unit test, который показывает, что он относится к номерам, требующим более 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