任逍遥

 找回密码
 立即注册
查看: 30562|回复: 100

由double类型判等引发的一点小思考

    [复制链接]
  • TA的每日心情
    慵懒
    2021-11-26 16:44
  • 签到天数: 488 天

    连续签到: 1 天

    [LV.9]妙领天机

    488

    主题

    3345

    帖子

    4456

    积分

    如雷贯耳

    Rank: 8Rank: 8

    积分
    4456
    发表于 2018-7-23 03:30:06 | 显示全部楼层 |阅读模式

      其实这篇文章很早就想发出来的,由于工作比较忙碌,所以一直也没写,最近有点空闲时间了,所以趁此整理一下,分享出来。
      今年以来一直在做一个交易所的项目,其中有部分交易的需求需要通过汇总统计来进行数据的聚合筛选,而聚合筛选的数据就是double类型的,我当然知道double类型进行判等的时候会有点坑,直到后来,有些业务需要其他同事来协同,然后就有个同事问我代码里面,写的if(Math.Abs(x-y)<=1E-7)是什么意思,SQL语句里面也这样写的,类似于这样的:
      double[]
      price = { 0.1, 0.2, 0.3, 0.8, 0.5 };
      double sum = 1.9;
      if (Math.Abs(sum - price.Sum()) <= 1E-7)
      {
      //todo
      }
      我解释说:double类型可能会有精度溢出的情况,导致在判等的时候会有丢失精度的问题,直接用“==”判等不行,所以就需要相减取绝对值再判断小于等于0.00000001的就认为它们是相等的。
      因为这个细节上的问题,公司让我新出的面试题我也加进去了,但最后评审的时候说这道题涉及到的不仅仅是C#的知识点,万一人家没学过计算机组成原理那不整来瓜起了,所以又把这道题剔除了。


      其实要究其真正原因,那还是得从计算机组成原理讲起。
      我们知道double双精度类型有53 位有效数字精度(包含符号号),并总共占用8字节。
      程序里面我们处理的大多是十进制,而计算机处理直接收二进制数据,学过进制转换的童鞋肯定知道整数都可以完美地进行进制间的转换,而double是小数,小数的进制转换就会出现循环小数的情况,而double的位数有限,就导致了十进制小数转二进制时出现了精度丢失的情况。
      经典案例:0.1+0.2-0.3=5.551115123125783e-17?。
      why?0.1+0.2-0.3居然不等于0?!!

      来,把它们都转换成二进制来看看:
      十进制0.1
      >二进制0.00011001100110011…(循环0011)
      >尾数为1.1001100110011001100…1100(共52位,除了小数点左边的1),指数为-4(二进制移码为00000000010),符号位为0
      >计算机存储为:0 00000000100 10011001100110011…11001
      > 因为尾数最多52位,所以实际存储的值为0.00011001100110011001100110011001100110011001100110011001
      再看看十进制0.2
      >二进制0.0011001100110011…(循环0011)
      >尾数为1.1001100110011001100…1100(共52位,除了小数点左边的1),指数为-3(二进制移码为00000000011),符号位为0
      >存储为:0 00000000011 10011001100110011…11001
      因为尾数最多52位,所以实际存储的值为0.00110011001100110011001100110011001100110011001100110011

      十进制0.3
      >按照上面相同的套路,得出来:0.0011001100110011001100110011001100110011001100110011,结尾的0011循环。
      那么两者相加得: 0.00011001100110011001100110011001100110011001100110011001+0.00110011001100110011001100110011001100110011001100110011=0.01001100110011001100110011001100110011001100110011001100
      也就是说只要0.3在最低位加一的话就和0.2+0.1一样了,而尾数的最低位是第52位,再乘上-2的阶码,就是2的负54次方,这个数刚好就是:5.551115123125783e-17。
      所以如果要判等0.1+0.2-0.3和0,不能直接用“==”,必须相减取绝对值!

      特别提醒:
      这绝对不仅仅限于C#,所有的编程语言的double都需要这样去判等,包括js、Python等弱类型语言(浮点型),谨记!

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    楼主热帖
  • TA的每日心情
    慵懒
    2019-8-30 15:02
  • 签到天数: 469 天

    连续签到: 3 天

    [LV.9]妙领天机

    5

    主题

    2943

    帖子

    2946

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2946
    发表于 2018-7-23 03:42:02 | 显示全部楼层
    路过 帮顶 嘿嘿
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2019-8-30 12:49
  • 签到天数: 468 天

    连续签到: 6 天

    [LV.9]妙领天机

    4

    主题

    2918

    帖子

    2919

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2919
    发表于 2018-7-25 17:01:29 | 显示全部楼层
    发发呆,回回帖,工作结束~
    回复

    使用道具 举报

  • TA的每日心情

    2019-8-29 18:19
  • 签到天数: 462 天

    连续签到: 2 天

    [LV.9]妙领天机

    0

    主题

    2971

    帖子

    2971

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2971
    发表于 2018-8-1 10:27:33 | 显示全部楼层
    楼下的接上。。。。
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2019-8-30 16:45
  • 签到天数: 464 天

    连续签到: 8 天

    [LV.9]妙领天机

    0

    主题

    2938

    帖子

    2932

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2932
    发表于 2018-8-1 17:01:19 | 显示全部楼层
    我了个去,顶了
    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    2019-8-30 16:26
  • 签到天数: 475 天

    连续签到: 1 天

    [LV.9]妙领天机

    10

    主题

    2910

    帖子

    2914

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2914
    发表于 2018-8-1 21:25:29 | 显示全部楼层
    支持,楼下的跟上哈~
    回复

    使用道具 举报

  • TA的每日心情
    难过
    2019-8-30 19:44
  • 签到天数: 456 天

    连续签到: 2 天

    [LV.9]妙领天机

    2

    主题

    2935

    帖子

    2934

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2934
    发表于 2018-8-2 12:57:47 | 显示全部楼层
    是爷们的娘们的都帮顶!大力支持
    回复

    使用道具 举报

  • TA的每日心情
    擦汗
    2019-8-30 15:14
  • 签到天数: 484 天

    连续签到: 1 天

    [LV.9]妙领天机

    1

    主题

    2949

    帖子

    2948

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2948
    发表于 2018-8-5 09:35:32 | 显示全部楼层
    看起来不错
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2019-8-30 19:33
  • 签到天数: 475 天

    连续签到: 1 天

    [LV.9]妙领天机

    6

    主题

    2979

    帖子

    2978

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2978
    发表于 2018-8-6 22:55:42 | 显示全部楼层
    佩服佩服!
    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    2019-8-30 05:55
  • 签到天数: 471 天

    连续签到: 2 天

    [LV.9]妙领天机

    9

    主题

    2957

    帖子

    2964

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2964
    发表于 2018-8-9 23:22:38 | 显示全部楼层
    我是个凑数的。。。
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|手机版|小黑屋|任逍遥

    GMT+8, 2024-5-5 17:19 , Processed in 0.079282 second(s), 50 queries .

    Powered by 任逍遥 X3.4

    Copyright © 2001-2023, Rxiaoyao Cloud.

    快速回复 返回顶部 返回列表