petelin
V2EX  ›  数学

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

  •  
  •   petelin · Jul 7, 2017 · 7914 views
    This topic created in 3415 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
    petelin
    OP
    Jul 7, 2017
    1
    加一个条件, 参加计算的小数,标度最大为 2, 也就是说不会有 0.001 这种
    coderfox
    coderfox
    Jul 7, 2017 via Android
    2
    是要存金钱吗?

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

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

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

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