Search code examples
iosobjective-cnsdictionarynsdatansjsonserialization

NSNumber value changing when JSON object is converted to data and then that data is again converted back to JSON object


I am creating NSData object with a dictionary as follows,

dictionary:

{
    channelId = 201;
    lang = EN;
    reqRefNo = vZRIzNd;
    storeInfo =     {
        clientInfo =         {
            deviceId = XXXXXXXXA67F488B836D19D80EC4FD8D;
        };
        loginLogId = XXXXXXXX5CAD4A0DA06BE2F055929856;
        qrCodeInfo =         {
            itemDesc = "";
            itemName = hahsj;
            price = 106;
        };
        userInfo =         {
            storeId = 3252;
        };
    };
}

price = 106

NSData *data = [NSJSONSerialization dataWithJSONObject:paramDict options:kNilOptions error:nil];

Now I'm again converting it back to JSON object as follows,

NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data
                                                         options:kNilOptions
                                                       error:&error];

But I'm getting wrong price this time.

converted dictionary

{
    channelId = 201;
    lang = EN;
    reqRefNo = vZRIzNd;
    storeInfo =     {
        clientInfo =         {
            deviceId = XXXXXXXXA67F488B836D19D80EC4FD8D;
        };
        loginLogId = XXXXXXXX5CAD4A0DA06BE2F055929856;
        qrCodeInfo =         {
            itemDesc = "";
            itemName = hahsj;
            price = "105.9999923706055";
        };
        userInfo =         {
            storeId = 3252;
        };
    };
}

price = "105.9999923706055"

Code for generating NSNumber,

//_amountTF.text = "S$1.06";
NSString *amount = [_amountTF.text stringByReplacingOccurrencesOfString:@"S$" withString:@""];
float amt = [amount floatValue];
amt *=100;
NSNumber *num = [NSNumber numberWithFloat:amt];

Can anyone please explain me the reason behind this? Thanks.


Solution

  • It is a floating point precision problem. Use double instead of float.

    Your number is stored in a binary format that can't represent all decimal values exactly, so some values are "rounded" to its nearest binary value. When printing the number, it will convert your float to double first, then print it. The conversion to double will take your rounded binary representation and store with higher precision, making it high precision with low precision content in it. Printing this will show the approximation you got when using float precision.

    NSString *amount = [_amountTF.text stringByReplacingOccurrencesOfString:@"S$" withString:@""];
    double amt = [amount doubleValue];
    amt *=100;
    NSNumber *num = [NSNumber numberWithDouble:amt];
    

    Read more: 'float' vs. 'double' precision