Consider this code:
NSNumber* interchangeId = dict[@"interchangeMarkerLogId"];
long long llValue = [interchangeId longLongValue];
double dValue = [interchangeId doubleValue];
NSNumber* doubleId = [NSNumber numberWithDouble:dValue];
long long llDouble = [doubleId longLongValue];
if (llValue > 1000000) {
    NSLog(@"Have Marker iD = %@,  interchangeId = %@, long long value = %lld, doubleNumber = %@, doubleAsLL = %lld, CType = %s, longlong = %s", self.iD, interchangeId, llValue, doubleId, llDouble, [interchangeId objCType], @encode(long long));
}
The results:
Have Marker iD = (null), interchangeId = 635168520811866143, long long value = 635168520811866143, doubleNumber = 6.351685208118661e+17, doubleAsLL = 635168520811866112, CType = d, longlong = q
dict is coming from NSJSONSerialization, and the original JSON source data is "interchangeId":635168520811866143.  It appears that all 18 digits of the value have been captured in the NSNumber, so it could not possibly have been accumulated by NSJSONSerialization as a double (which is limited to 16 decimal digits).  Yet, objCType is reporting that it's a double.
We find this in the documentation for NSNumber: "The returned type does not necessarily match the method the receiver was created with." So apparently this is a "feechure" (i.e., documented bug).
So how can I determine that this value originated as an integer and not a floating point value, so I can extract it correctly, with all the available precision? (Keep in mind that I have some other values that are legitimately floating-point, and I need to extract those accurately as well.)
I've come up with two solutions so far:
The first, which does not make use of knowledge of NSDecimalNumber --
NSString* numberString = [obj stringValue];
BOOL fixed = YES;
for (int i = 0; i < numberString.length; i++) {
    unichar theChar = [numberString characterAtIndex:i];
    if (theChar != '-' && (theChar < '0' || theChar > '9')) {
        fixed = NO;
        break;
    }
}
The second, which assumes that we only need worry about NSDecimalNumber objects, and can trust the CType results from regular NSNumbers --
if ([obj isKindOfClass:[NSDecimalNumber class]]) {
    // Need to determine if integer or floating-point.  NSDecimalNumber is a subclass of NSNumber, but it always reports it's type as double.
    NSDecimal decimalStruct = [obj decimalValue];
    // The decimal value is usually "compact", so may have a positive exponent even if integer (due to trailing zeros).  "Length" is expressed in terms of 4-digit halfwords.
    if (decimalStruct._exponent >= 0 && decimalStruct._exponent + 4 * decimalStruct._length < 20) {
        sqlite3_bind_int64(pStmt, idx, [obj longLongValue]);            
    }
    else {
        sqlite3_bind_double(pStmt, idx, [obj doubleValue]);           
    }
}
else ... handle regular NSNumber by testing CType.
The second should be more efficient, especially since it does not need to create a new object, but is slightly worrisome in that it depends on "undocumented behavior/interface" of NSDecimal -- the meanings of the fields are not documented anywhere (that I can find) and are said to be "private".
Both appear to work.
Though on thinking about it a bit -- The second approach has some "boundary" problems, since one can't readily adjust the limits to assure that the maximum possible 64-bit binary int will "pass" without risking loss of a slightly larger number.
Rather unbelievably, this scheme fails in some cases:
BOOL fixed = NO;
long long llValue = [obj longLongValue];
NSNumber* testNumber = [[NSNumber alloc] initWithLongLong:llValue];
if ([testNumber isEqualToNumber:obj]) {
    fixed = YES;
}
I didn't save the value, but there is one for which the NSNumber will essentially be unequal to itself -- the values both display the same but do not register as equal (and it is certain that the value originated as an integer).
This appears to work, so far:
BOOL fixed = NO;
if ([obj isKindOfClass:[NSNumber class]]) {
     long long llValue = [obj longLongValue];
     NSNumber* testNumber = [[[obj class] alloc] initWithLongLong:llValue];
     if ([testNumber isEqualToNumber:obj]) {
         fixed = YES;
     }
 }
Apparently isEqualToNumber does not work reliably between an NSNumber and an NSDecimalNumber.
(But the bounty is still open, for the best suggestion or improvement.)
 
     
     
     
     
     
    