V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
lyf362345
V2EX  ›  C

两个 double 做比较, 为什么加要个 1e-5 呢

  •  
  •   lyf362345 · 2015-05-04 14:29:57 +08:00 · 2003 次点击
    这是一个创建于 3493 天前的主题,其中的信息可能已经有所发展或是发生改变。
    初学c++, 做练习题的时候跟答案对比, 发现有点不一样

    练习题是:

    描述
    在北大校园里, 没有自行车, 上课办事会很不方便. 但实际上, 并非去办任何事情都是骑车快,
    因为骑车总要找车、开锁、停车、锁车等, 这要耽误一些时间. 假设找到自行车,
    开锁并骑上自行车的时间为27秒; 停车锁车的时间为23秒; 步行每秒行走1.2米,
    骑车每秒行走3.0米. 请判断走不同的距离去办事, 是骑车快还是走路快.

    输入
    第一行为待处理的数据的数量n
    其后每一行整数为一次办事要行走的距离,单位为米.

    输出
    对应每个整数,如果骑车快,输出一行"Bike";如果走路快,输出一行"Walk";如果一样快,输出一行"All".

    样例输入
    4
    50
    90
    120
    180

    样例输出
    Walk
    Walk
    Bike
    Bike

    我的代码


    答案里定义了个 `double delta = 1e-5` 然后比较的时候 再加上去, 这里有什么用呢
    第 1 条附言  ·  2015-05-04 15:04:54 +08:00
    根据一楼的指导, 17行已经改为 `bikeTime = m / 3.0 + 27 + 23;`
    第 2 条附言  ·  2015-05-05 10:42:56 +08:00
    答案里 比较大小都加 delta 了, All 的情况我没写.
    31 条回复    2015-05-05 21:33:55 +08:00
    dslwind
        1
    dslwind  
       2015-05-04 14:43:29 +08:00 via Android   ❤️ 1
    大概是因为浮点数在计算机内部的存储和表示的问题,并不是所有浮点数都能“精确”的等于你想要的值。ps. biketime应该等于m/3.0+27+23。/3是整除……
    acros
        2
    acros  
       2015-05-04 14:45:38 +08:00   ❤️ 1
    同楼上。
    delta这个名字就表明了意思:误差范围。
    zhicheng
        3
    zhicheng  
       2015-05-04 14:48:29 +08:00   ❤️ 1
    如果两个 double 很接近,不能直接进行比较。细节太多,楼主多 Google 一下。
    如果需要,我有一个封装好的可以直接拿来用。
    https://github.com/zhicheng/fequal
    cover
        4
    cover  
       2015-05-04 14:50:45 +08:00   ❤️ 1
    double的误差范围把。。比如你在做几何学运算的时候,两个角度相差 0.0001度的时候你就认为两个角度相等的意思
    lyf362345
        5
    lyf362345  
    OP
       2015-05-04 15:00:42 +08:00
    @dslwind 嗯 谢谢
    lyf362345
        6
    lyf362345  
    OP
       2015-05-04 15:01:31 +08:00
    @zhicheng 水很深呐, 我研究下你的代码
    lyf362345
        7
    lyf362345  
    OP
       2015-05-04 15:02:00 +08:00
    @cover 明白了, 非常感谢
    lyf362345
        8
    lyf362345  
    OP
       2015-05-04 15:02:43 +08:00
    @acros 恩恩 谢谢
    msg7086
        9
    msg7086  
       2015-05-04 15:11:47 +08:00
    10进制的有限小数在2进制里不一定就是有限小数。
    比如1.1(循环小数)+1.2(循环小数) 与 1.3(循环小数) 就不一定会相等,而是在最尾端可能会出现一个很小的误差。
    lyf362345
        10
    lyf362345  
    OP
       2015-05-04 15:25:40 +08:00
    @msg7086 嗯 谢谢啊
    chai2010
        11
    chai2010  
       2015-05-04 15:57:01 +08:00
    Elethom
        12
    Elethom  
       2015-05-04 16:14:54 +08:00 via iPhone
    沒人吐槽應該是「both」麼?
    comicfans44
        13
    comicfans44  
       2015-05-04 16:17:55 +08:00
    浮点误差只有在比较相等的时候会用到,比如你判断
    if(abs(walkTime-bikeTime)<delta){
    cout<<"bike walk time equal"
    }
    可是现在你的逻辑是比较大小,当然是直接比较值,不明白答案为什么有delta。
    如果说进制不同导致的计算误差用一个delta来补足,显然也是不合适的。
    1e-5就不能用double准确表示(这本身就是一个十进制小数),怎么能假定加上一个不准确的delta就能让结果绝对准确?怎么能确定计算结果正好少了delta?如果需要绝对的准确性,那不应该加上delta,而是使用有理数进行计算。个人觉得这个delta是不合理的。
    WKPlus
        14
    WKPlus  
       2015-05-04 16:23:55 +08:00
    @comicfans44 对呀,不是浮点数用来判断是否相等的时候才需要引入一个delta的么?
    Neveroldmilk
        15
    Neveroldmilk  
       2015-05-04 17:08:17 +08:00
    浮点数在计算机里不是精确存储的,会有误差,所以要设定阈值。
    koykoi
        16
    koykoi  
       2015-05-04 17:17:59 +08:00
    比较大小要加什么阈值...
    即使是 tolerance 设为 1e-5 也没什么道理
    chai2010
        17
    chai2010  
       2015-05-04 17:35:33 +08:00
    fix typo: IEEE854 -> IEEE754
    lyf362345
        18
    lyf362345  
    OP
       2015-05-04 20:02:43 +08:00
    @Elethom both 那不就是都不加了么
    lyf362345
        19
    lyf362345  
    OP
       2015-05-04 20:04:39 +08:00
    @comicfans44
    @WKPlus
    @koykoi

    几位的回答又让我不确定了...
    CRVV
        20
    CRVV  
       2015-05-04 23:38:41 +08:00
    题目里说:如果一样快,输出一行"All"

    我咋没在代码里看到"All"...
    判断相等需要用阈值

    而且这题直接和100比较,大于100骑车,小于100走路,关浮点数什么事
    canautumn
        21
    canautumn  
       2015-05-05 02:22:24 +08:00
    这个答案可能不是很好。按理说判断大小不用考虑delta的,只有判断相等的时候才用delta。不过估计这个答案的原作者在m/3.0这儿写错写成了m/3,导致通不过oj,然后手动加了一个delta的hack才通过oj(我猜的,没测试)。还有一种可能是double精度太高,3.0实际存储的值是2.999999....,导致需要用delta来应对某个特殊的test case。想钻研的话可以看看这个 http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html (不负责任推荐,我也没看过,暂时没遇到需要考虑这些东西的情况)
    laoyur
        22
    laoyur  
       2015-05-05 09:11:59 +08:00
    @Elethom 槽点比较独特,不过你是√的,:)
    lyf362345
        23
    lyf362345  
    OP
       2015-05-05 10:56:59 +08:00
    @canautumn 我更新了下代码, 按答案的写法, 貌似不是写错; 文档我找了分中文的
    canautumn
        24
    canautumn  
       2015-05-05 10:58:27 +08:00
    @lyf362345 那就是第二个原因。我这边搜到的答案有说用float就可以通过oj,double就不行的。但没搜到使用delta的答案。
    CRVV
        25
    CRVV  
       2015-05-05 16:22:35 +08:00
    相等的情况没写,和写了相等的情况,是两回事
    先用阈值判断出来1.99999999和2相等,就不用比大小了
    不判断相等,直接比1.99999999和2,结果是不相等
    做题就按原题来做呗,何必自己精简题目
    CRVV
        26
    CRVV  
       2015-05-05 16:29:55 +08:00
    原来在代码结尾加上了else输出"All"...
    这题的每一个比较都是在大于、小于和等于3种情况里选择,所以每一步都需要阈值
    不过我还是觉得这题适合用整数来计算
    lyf362345
        27
    lyf362345  
    OP
       2015-05-05 18:43:47 +08:00
    lyf362345
        28
    lyf362345  
    OP
       2015-05-05 18:48:28 +08:00
    @CRVV All 部分是我后面加上的, 类似13楼的说法是 这个阀值是作者容忍的阀值还是计算机计算误差的阀值呢

    觉得如果题目有个前提就好了, 关于阀值部分, 不然我可以完全不管误差, 因为在实际应用的时候是会人为的去掉这个误差的

    我也不纠结了
    CRVV
        29
    CRVV  
       2015-05-05 19:21:51 +08:00
    @lyf362345



    我觉得你并没有搞懂浮点数的事,上面有人发链接了,先看明白再说吧
    至少要知道下面这个情况的原因,Python 3.4.3
    >>> 1.2-0.1
    1.0999999999999999

    这道题用1e-5得到正确的结果没有问题,代码应该也没错,至于1e-5这数选得好不好就另说了
    lyf362345
        30
    lyf362345  
    OP
       2015-05-05 21:33:44 +08:00
    @CRVV 好的
    lyf362345
        31
    lyf362345  
    OP
       2015-05-05 21:33:55 +08:00
    @CRVV 谢谢
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5635 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 1072ms · UTC 07:36 · PVG 15:36 · LAX 23:36 · JFK 02:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.