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

只需要保证小数点后两位计算正确, 用 float 有问题吗?

  •  
  •   petelin · 2017-07-07 17:31:38 +08:00 · 6598 次点击
    这是一个创建于 2694 天前的主题,其中的信息可能已经有所发展或是发生改变。

    用浮点数表达十进制中的小数会出现误差, 但是这个误差极小, 基本上计算出来的都是 0.xx00000000xxx 或者 0.xx99999999xxx, 这个时候我们只取小数点后两位应该是很精确地, 我想不到什么时候这样会出问题, 求指教

    25 条回复    2017-07-10 21:04:24 +08:00
    petelin
        1
    petelin  
    OP
       2017-07-07 17:34:28 +08:00
    加一个条件, 参加计算的小数,标度最大为 2, 也就是说不会有 0.001 这种
    coderfox
        2
    coderfox  
       2017-07-07 17:41:07 +08:00 via Android
    是要存金钱吗?

    位数确定的小数,在不超过数据类型范围的情况下还是建议存成整数。
    一方面是肯定不会出现精度损失;
    另一方面是整型的计算、序列化、反序列化都比浮点数快。
    choury
        3
    choury  
       2017-07-07 17:42:32 +08:00
    直接乘以 100 存成整数多好
    h4x3rotab
        4
    h4x3rotab  
       2017-07-07 17:56:59 +08:00 via iPhone
    原因是浮点表示实际是二进制小数,很多十进制小数在二进制下就是循环小数。资金要用 fixed 表示。
    littleylv
        5
    littleylv  
       2017-07-07 18:00:39 +08:00
    现在一般建议价格等金额的数据存整数
    af463419014
        6
    af463419014  
       2017-07-07 18:07:36 +08:00
    用 decimal,不会出现浮点数计算的误差
    snnn
        7
    snnn  
       2017-07-07 18:12:16 +08:00 via Android
    看你要做什么操作了。只是加减法就还好
    archer2ee
        8
    archer2ee  
       2017-07-07 18:13:27 +08:00 via iPhone
    如果是金钱的话,存分!!我做支付用 bigdecimal 也感觉各种麻烦。float 就是禁区了。
    BigBearWatchYou
        9
    BigBearWatchYou  
       2017-07-07 18:52:49 +08:00
    你不妨试试 System.out.println(999999.99f);

    建议:
    1.float 最多保证 7 位有效数字,如果超过了,就不要用 float,而是使用 bigdecimal (这里的有效数字指:从该数高位的第一个非零数字起,直到低位末尾的小数非零数字或个位止的数字)
    2.因为 bigdecimal 的性能不好,如果是跟钱有关,只可能有两位小数,那建议直接用 long 存分,在需要显示的地方格式化
    BigBearWatchYou
        10
    BigBearWatchYou  
       2017-07-07 18:57:10 +08:00
    而且如果要进行四则运算,因为有对阶过程,float 连 7 位有效数字的精度也无法保证
    bear2017
        11
    bear2017  
       2017-07-07 21:26:30 +08:00 via Android
    是涉及到钱吗?涉及钱的话用分做单位,显示客户端统一除 100。不要用 float,会有问题的。
    rrfeng
        12
    rrfeng  
       2017-07-07 21:59:06 +08:00
    这个直接 * 100 存整数。
    只要是小数,不管你几位总会出现误差的。
    gogohigh
        13
    gogohigh  
       2017-07-07 22:09:04 +08:00
    不行。float 只是近似值,用 String 或者*100 存整形
    xenme
        14
    xenme  
       2017-07-07 22:21:17 +08:00
    歪个楼:
    上面好多出现一分钱抢购的是不是都是你们存分,然后计算的时候没有乘以 100,直接卖了啊?
    akira
        15
    akira  
       2017-07-07 22:21:35 +08:00
    没有问题。
    mingyun
        16
    mingyun  
       2017-07-07 23:06:32 +08:00
    参考微信用分做单位
    zoudm
        17
    zoudm  
       2017-07-07 23:48:31 +08:00
    一个经典样例。
    #include <stdio.h>
    int main() {
    float t = 0.1;
    float sum = 0;
    for (int i = 0; i < 100000; i++) {
    sum += t;
    }
    printf("sum: %f\n", sum);
    return 0;
    }

    gcc -o testfloat testfloat.c
    ./testfloat
    sum: 9998.556641

    准确结果应该为 10000,实际为 9998.556641
    hjc4869
        18
    hjc4869  
       2017-07-08 00:52:11 +08:00 via Android
    问题很大,楼主的需求明显是要用定点数。
    kx5d62Jn1J9MjoXP
        19
    kx5d62Jn1J9MjoXP  
       2017-07-08 07:59:20 +08:00 via Android
    有,较大的数,float 连整数都无法全部覆盖
    chinawrj
        20
    chinawrj  
       2017-07-08 10:24:18 +08:00 via Android
    不要用浮点,不要用浮点,不要用浮点!直接倍数之后用整型!或者有金融 math 库
    petelin
        21
    petelin  
    OP
       2017-07-09 18:37:51 +08:00
    @zoudm 为什么这个误差这么大, 我用 python 实验的时候, `sum(0.1 for i in range(100000))` 结果是 10000.000000018848, 我觉得可以接受啊
    petelin
        22
    petelin  
    OP
       2017-07-09 18:38:17 +08:00
    确实是存金额, 老大不让用分.........
    petelin
        23
    petelin  
    OP
       2017-07-09 18:41:55 +08:00
    @BigBearWatchYou 试了一下是 1000000.0, 这样的话在 java 里肯定不能用了. 现在比较好奇为什么 python 对浮点数处理这么好,
    In [29]: Decimal(float(99999.99))
    Out[29]: Decimal('99999.990000000005238689482212066650390625')
    误差都很小的, 包括上面一位同学给出的 C 代码, 在 python 下精度也很高.我去搜搜
    petelin
        24
    petelin  
    OP
       2017-07-09 18:49:45 +08:00
    @petelin Python 下浮点数都是用双精度存的,所以精度会高一点, 这个时候 99999999999999.99 就开始不精确了
    In [38]: 99999999999999.99 + 99999999999999.99
    Out[38]: 199999999999999.97

    我觉得如果你的系统任何一处能算到这么大的数, 那就不能用了, 如果没有的还是可以的用的, 0.1d 在怎么加,只要小数点后两位也没什么问题.

    ps: 都是个人直觉...实际工作还是存分吧, 或者老大说怎么搞就怎么搞 :)
    BigBearWatchYou
        25
    BigBearWatchYou  
       2017-07-10 21:04:24 +08:00
    @petelin IEEE 754 协议规定的,大部分语言里都遵循这个,单精度保证 7 位,双精度保证 15 位
    具体计算细节我之前思考过,还挺有意思的。不只是直接 N*lg2,而是整数部分看大小,小数部分看舍入,然后一块计算,但是最后结果也是近似 N*lg2,有空我可以整理一下
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2799 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 15:05 · PVG 23:05 · LAX 07:05 · JFK 10:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.