V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
qq292382270
V2EX  ›  程序员

求助众位前辈,19.99 * 100 - 1999 算式为什么结果是-2.2737367544323E-13 ?

  •  
  •   qq292382270 · 2019-07-22 18:23:49 +08:00 · 3666 次点击
    这是一个创建于 1984 天前的主题,其中的信息可能已经有所发展或是发生改变。

    脑壳疼,测试段代码需要用到个算式 : 19.99 * 100 - 1999,结果为-2.2737367544323E-13;
    算式中的 19.99 可以以变量形式传递进来,也还是这个结果. 用 19.98100-1998(或其他两位小数的数字减去它乘 100 的数字) 都正常为 0.
    算式在 java php js 中都为这个结果,感觉 19.99 好诡异,各位前辈有遇到过这样的情况么,有没有好的解决方案来避免.目前我的测试是用命令(php) sprintf("%.0f", 19.99
    100)才能正常执行..
    (咦~好奇怪,上面这段文本突然斜体了...)

    20 条回复    2019-07-23 14:27:28 +08:00
    limuyan44
        1
    limuyan44  
       2019-07-22 18:33:11 +08:00 via Android   ❤️ 1
    java 中有专门用于高精度计算的类型,php 料想应该也有,通常涉及浮点型本身就是近似计算。
    ipwx
        2
    ipwx  
       2019-07-22 18:36:19 +08:00   ❤️ 1
    月经问题。。。

    内存中浮点数是二进制存储的。一个有限十进制小数不一定存在对应的有限二进制小数。事实上除了能写成“某个整数 + sum_{i=1}^k a_i 2^{-i}”这种形式的小数,都不能写成有限的二进制小数。

    而计算机的浮点数有长度限制,一个无限循环二进制小数截断以后,就和原来的十进制小数并不完全相等了。你减出来的这个值,就是截断误差。
    johnniang
        3
    johnniang  
       2019-07-22 18:38:01 +08:00 via Android   ❤️ 1
    建议看看 《计算机组成原理》。
    Rekkles
        4
    Rekkles  
       2019-07-22 18:38:39 +08:00   ❤️ 1
    php 请用 bcmath
    echo bcsubbcmul(19.99,100,2),1999);
    Raymon111111
        5
    Raymon111111  
       2019-07-22 18:59:56 +08:00   ❤️ 1
    简单讲

    你看见的 19.99 十进制下人畜无害, 有可能在二进制下是一个无限循环小数 (可以自己做一下进制转换)

    这样一来, 就算乘以 100 和后面的数相减也得不到 0
    ClericPy
        6
    ClericPy  
       2019-07-22 19:15:33 +08:00   ❤️ 1
    搜一下 浮点数 IEEE
    laodao1990
        7
    laodao1990  
       2019-07-22 19:24:25 +08:00   ❤️ 1
    推荐看看《深入理解计算机系统》
    通俗点解释就是,计算机是用二进制表示的,对于某些小数和人类用 10 进制无法准确表示 1/3 是一个意思,会有误差。
    VDimos
        8
    VDimos  
       2019-07-22 19:31:01 +08:00 via Android   ❤️ 1
    真—月经问题
    Kirscheis
        9
    Kirscheis  
       2019-07-22 19:40:06 +08:00 via Android   ❤️ 3
    f64(19.99) = 0x1.3fd70a3d70a3dp+4

    f64(100.0) = 0x1.9000000000000p+6

    0x1.3fd70a3d70a3dp+4 * 0x1.9000000000000p+6 = 0x1.f3bffffffffffp+10

    f64(1999.0) = 0x1.f3c0000000000p+10

    所以结果是

    0x1.f3c0000000000p+10 - 0x1.f3bffffffffffp+10 = -0x1.0000000000000p-42

    转换到 10 进制就是 -2.2737367544323206e-13
    Kirscheis
        10
    Kirscheis  
       2019-07-22 19:41:34 +08:00 via Android
    上面打多一个负号,凑合看
    joshu
        11
    joshu  
       2019-07-22 19:42:01 +08:00 via Android   ❤️ 1
    浮点数的比较一般是小于 1e-N ( N 多少我忘了)就视为相等
    不要比较浮点数相等
    akazure
        12
    akazure  
       2019-07-22 19:42:40 +08:00 via Android   ❤️ 1
    一般采取新建一个 equal 方法,并定义一个极小量,以作为误差范围。
    jamesliu96
        13
    jamesliu96  
       2019-07-22 19:43:11 +08:00 via Android   ❤️ 1
    IEEE 754
    Mistwave
        14
    Mistwave  
       2019-07-22 19:48:20 +08:00 via iPhone   ❤️ 1
    littlewing
        15
    littlewing  
       2019-07-22 19:50:19 +08:00   ❤️ 1
    手动搜索 “ IEEE754 浮点数”
    heart4lor
        16
    heart4lor  
       2019-07-22 20:29:37 +08:00   ❤️ 1
    浮点数存储问题都不知道的么,斜体是因为 markdown 语法
    xuanbg
        17
    xuanbg  
       2019-07-22 20:41:34 +08:00   ❤️ 1
    一般的业务场景有小数的时候不要用浮点数,不要用浮点数,不要用浮点数。重要的事情说 3 遍。因为我们默认计算的结果是精确的,而浮点数是有精度问题的,不能满足精确的要求。
    msg7086
        18
    msg7086  
       2019-07-22 23:27:42 +08:00   ❤️ 1
    小数通常是近似值不是精确值。
    TobiahShaw
        19
    TobiahShaw  
       2019-07-23 09:48:37 +08:00   ❤️ 1
    因为不连续,你可以判断 19.99 * 100 - 1999 < Epsilon,或者使用 BigDecimal
    brust
        20
    brust  
       2019-07-23 14:27:28 +08:00   ❤️ 1
    精度问题 浮点数 计算会丢失精度
    java 的话推荐<码出高效>这本书
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3659 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 10:20 · PVG 18:20 · LAX 02:20 · JFK 05:20
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.