petelin
V2EX  ›  数学

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

  •  
  •   petelin · Jul 7, 2017 · 7480 views
    This topic created in 3269 days ago, the information mentioned may be changed or developed.

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

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

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

    建议:
    1.float 最多保证 7 位有效数字,如果超过了,就不要用 float,而是使用 bigdecimal (这里的有效数字指:从该数高位的第一个非零数字起,直到低位末尾的小数非零数字或个位止的数字)
    2.因为 bigdecimal 的性能不好,如果是跟钱有关,只可能有两位小数,那建议直接用 long 存分,在需要显示的地方格式化
    BigBearWatchYou
        10
    BigBearWatchYou  
       Jul 7, 2017
    而且如果要进行四则运算,因为有对阶过程,float 连 7 位有效数字的精度也无法保证
    bear2017
        11
    bear2017  
       Jul 7, 2017 via Android
    是涉及到钱吗?涉及钱的话用分做单位,显示客户端统一除 100。不要用 float,会有问题的。
    rrfeng
        12
    rrfeng  
       Jul 7, 2017
    这个直接 * 100 存整数。
    只要是小数,不管你几位总会出现误差的。
    gogohigh
        13
    gogohigh  
       Jul 7, 2017
    不行。float 只是近似值,用 String 或者*100 存整形
    xenme
        14
    xenme  
       Jul 7, 2017
    歪个楼:
    上面好多出现一分钱抢购的是不是都是你们存分,然后计算的时候没有乘以 100,直接卖了啊?
    akira
        15
    akira  
       Jul 7, 2017
    没有问题。
    mingyun
        16
    mingyun  
       Jul 7, 2017
    参考微信用分做单位
    zoudm
        17
    zoudm  
       Jul 7, 2017
    一个经典样例。
    #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  
       Jul 8, 2017 via Android
    问题很大,楼主的需求明显是要用定点数。
    kx5d62Jn1J9MjoXP
        19
    kx5d62Jn1J9MjoXP  
       Jul 8, 2017 via Android
    有,较大的数,float 连整数都无法全部覆盖
    chinawrj
        20
    chinawrj  
       Jul 8, 2017 via Android
    不要用浮点,不要用浮点,不要用浮点!直接倍数之后用整型!或者有金融 math 库
    petelin
        21
    petelin  
    OP
       Jul 9, 2017
    @zoudm 为什么这个误差这么大, 我用 python 实验的时候, `sum(0.1 for i in range(100000))` 结果是 10000.000000018848, 我觉得可以接受啊
    petelin
        22
    petelin  
    OP
       Jul 9, 2017
    确实是存金额, 老大不让用分.........
    petelin
        23
    petelin  
    OP
       Jul 9, 2017
    @BigBearWatchYou 试了一下是 1000000.0, 这样的话在 java 里肯定不能用了. 现在比较好奇为什么 python 对浮点数处理这么好,
    In [29]: Decimal(float(99999.99))
    Out[29]: Decimal('99999.990000000005238689482212066650390625')
    误差都很小的, 包括上面一位同学给出的 C 代码, 在 python 下精度也很高.我去搜搜
    petelin
        24
    petelin  
    OP
       Jul 9, 2017
    @petelin Python 下浮点数都是用双精度存的,所以精度会高一点, 这个时候 99999999999999.99 就开始不精确了
    In [38]: 99999999999999.99 + 99999999999999.99
    Out[38]: 199999999999999.97

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

    ps: 都是个人直觉...实际工作还是存分吧, 或者老大说怎么搞就怎么搞 :)
    BigBearWatchYou
        25
    BigBearWatchYou  
       Jul 10, 2017
    @petelin IEEE 754 协议规定的,大部分语言里都遵循这个,单精度保证 7 位,双精度保证 15 位
    具体计算细节我之前思考过,还挺有意思的。不只是直接 N*lg2,而是整数部分看大小,小数部分看舍入,然后一块计算,但是最后结果也是近似 N*lg2,有空我可以整理一下
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   925 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 82ms · UTC 19:08 · PVG 03:08 · LAX 12:08 · JFK 15:08
    ♥ Do have faith in what you're doing.