Object精度缺失问题

项目中会遇见的问题

在项目中,一些小数计算往往不太注意,但是当遇到的一些精度计算问题,令人抓狂,Apple建议使用NSDecimalNumber做处理, 我对此作了一点点封装XXDecimalNumberHandle

在涉及到和钱有关的计算时或者浮点数在总数中所占百分比计算,这些都是对精度要求严格的,
而使用floatValue doubleValue的转化运算时,往往会出现误差

我们知道在计算机的存储中,任何数据都是以0、1的形式被存储记录,每一个这样的存储单位叫做位(bit),这也是二进制的实现基础。
存储时,double和float是一样的,不一样的地方是位数不同,float是32位,double是64位,所以double存储的精度比float高
由于计算机在十进制中无法精确表示一些小数,比如0.6

         0.6
          *2
       --------
         1.2 ----> 1
          *2
       --------
         0.4 ----> 0
          *2
       --------
         0.8 ----> 0
          *2
       --------
         1.6 ----> 1
          *2
       --------
         1.2 ----> 1
          *2
       --------
         0.4 ----> 0
          *2
       --------
         0.8 ----> 0
          *2
       --------
         1.6 ----> 1
        ......
 11001100110011001100... 1100无穷尽

在实际项目中,当进行浮点型小数之间的比较时,会出现如下状况

if ([@"0.01" floatValue] < 0.01) {
    NSLog(@"我会被打印");
}

NSLog会被输出,无语了吧,0.01 < 0.01
因为编译器看到小数,默认作为double处理,而float和double表示的精度不一样

在项目中,精度问题,还会出现下方这种情况

double price = 78.6;
NSLog(@"%lf", price); // 78.600000
NSDictionary *dict = @{@"price" : @(price)};
NSLog(@"%@", dict);
/**
{
    price = "78.59999999999999";
}
*/

价格price打印出来竟然是78.59999999999999, 如果把这个字典作为键值传给服务器问题就是一个bug。

NSDecimalNumber

Apple建议使用NSDecimalNumber

double price = 78.6;
NSLog(@"%lf", price); // 78.600000
// 通过控制台po得到的结果是: po price --> 78.599999999999994
NSDecimalNumber *decimalNumber = [[NSDecimalNumber alloc] initWithDouble:price];
NSLog(@"%@", decimalNumber); // 78.59999999999999
// 通过控制台po得到的结果是: po decimalNumber --> 78.6
NSDictionary *dict = @{@"price" : @(price), @"decimalNumber" : decimalNumber};
NSLog(@"%@", dict);
/**
{
    decimalNumber = "78.6";
    price = "78.59999999999999";
}
*/

如此, 要正确的把值传给服务器, 干脆直接用NSDecimalNumber存入NSDictionary中得了。不过有时候, 我们希望把给服务器上传的字段封装在一个Model中, 但是经测试发现, 无论使用MJExtension还是YYModel, 还是有这个问题, 这并不是这些第三方框架的问题. 所以使用Model时作为传输媒介时, 也要注意这种问题.

附:

NSDecimalNumber简单介绍

NSDecimalNumber是NSNumber的子类,比NSNumber的功能更为强大,它们被设计为执行基础10计算,而不会损失精度并具有可预测的舍入行为。

苹果官方建议在货币以及要求精度很高的场景下使用NSDecimalNumber

+ (instancetype)decimalNumberHandlerWithRoundingMode:(NSRoundingMode)roundingMode scale:(short)scale raiseOnExactness:(BOOL)exact raiseOnOverflow:(BOOL)overflow raiseOnUnderflow:(BOOL)underflow raiseOnDivideByZero:(BOOL)divideByZero;

* 参数解析:
* roundingMode
 The rounding mode to use. There are four possible values: NSRoundUp, NSRoundDown, NSRoundPlain, and NSRoundBankers.
* scale
 The number of digits a rounded value should have after its decimal point.
* raiseOnExactness
 If YES, in the event of an exactness error the handler will raise an exception, otherwise it will ignore the error and return control to the calling method.
* raiseOnOverflow
 If YES, in the event of an overflow error the handler will raise an exception, otherwise it will ignore the error and return control to the calling method
* raiseOnUnderflow
 If YES, in the event of an underflow error the handler will raise an exception, otherwise it will ignore the error and return control to the calling method
* raiseOnDivideByZero
 If YES, in the event of a divide by zero error the handler will raise an exception, otherwise it will ignore the error and return control to the calling method

当试图除以0或产生一个数表示太大或太小的时候发生异常。
下面列出了各种异常的名字 表明NSDecimalNumber计算错误。
extern NSString *NSDecimalNumberExactnessException; //如果出现一个精确的错误
extern NSString *NSDecimalNumberOverflowException; // 溢出
extern NSString *NSDecimalNumberUnderflowException; //下溢
extern NSString *NSDecimalNumberDivideByZeroException; //除数为0

typedef NS_ENUM(NSUInteger, NSRoundingMode) {
    NSRoundPlain,   // Round up on a tie
    NSRoundDown,    // Always down == truncate
    NSRoundUp,      // Always up
    NSRoundBankers  // on a tie round so last digit is even
};

NSRoundPlain,   // Round up on a tie //四舍五入
NSRoundDown,    // Always down == truncate  //只舍不入
NSRoundUp,      // Always up    // 只入不舍
NSRoundBankers 四舍六入, 中间值时, 取最近的,保持保留最后一位为偶数yelp-api
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容