V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
mw717if
V2EX  ›  Python

Python 做除法算比例,结果加起来不是 100

  •  
  •   mw717if · 2020-03-31 10:32:09 +08:00 · 4710 次点击
    这是一个创建于 1733 天前的主题,其中的信息可能已经有所发展或是发生改变。

    '

        divisor = sum(list(dept_id2count.values()))
        
        divisor = str(divisor)
        
        getcontext().prec = 4
        
        dept_id2p = {}
        
        for dept_id in dept_id2count:
        
            count = dept_id2count[dept_id]
            
            dividend = str(100 * count)  # 被除数
            
            dept_id2p[dept_id] = Decimal(dividend) / Decimal(divisor)
            
            print(Decimal(dividend) / Decimal(divisor))
    

    计算代码如上,输出如下。算每个 count 占总数的比例,最后把比例存到 mysql,保留两位小数。 """

    1.781

    6.334

    0.009013

    0.9311

    0.02471

    0.1105

    14.28

    24.70

    0.4394

    11.09

    0.01872

    4.541

    0.06418

    13.75

    2.486

    1.953

    0.4429

    4.833

    4.242

    0.08313

    1.931

    0.7320

    0.06604

    1.762

    3.397

    99.99

    """

    问题是这些比例加起来不是 100,

    一开始用 float 保留两位小数,

    后来改成 decimal 四位有效数字,

    都没有解决。

    求助各位大佬,有没有什么好办法

    34 条回复    2020-03-31 18:58:35 +08:00
    viggoc
        1
    viggoc  
       2020-03-31 10:37:28 +08:00 via Android   ❤️ 1
    保留有效数字以后本来就没办法求和回复,想要精准就用两个数存成分数形式吧
    littleylv
        2
    littleylv  
       2020-03-31 10:41:39 +08:00   ❤️ 5
    想要保证 100 ?最后一个值用 100 减去前面的总和就行了
    mw717if
        3
    mw717if  
    OP
       2020-03-31 10:42:27 +08:00
    @viggoc 哎 目前的解决方案是保存提高精度:实际需要小数点后两位,但是保存了四位
    mw717if
        4
    mw717if  
    OP
       2020-03-31 10:53:15 +08:00
    @littleylv 精彩!!!
    SjwNo1
        5
    SjwNo1  
       2020-03-31 10:56:21 +08:00   ❤️ 1
    @littleylv 逻辑鬼才
    realpg
        6
    realpg  
       2020-03-31 10:59:19 +08:00   ❤️ 1
    "保留两位小数"
    whusnoopy
        7
    whusnoopy  
       2020-03-31 11:04:33 +08:00   ❤️ 2
    跟 Python 没关系啊,本来就是有精度损失的

    100/3 = 33.33
    100/3 = 33.33
    100/3 = 33.33

    33.33+33.33+33.33 = 99.99

    你取多少位精度最后都会差个 1
    jmc891205
        8
    jmc891205  
       2020-03-31 11:05:07 +08:00   ❤️ 1
    第一 这个事情跟 Python 没关系
    第二 你用一堆近似值求和得到的只会是近似值
    b821025551b
        9
    b821025551b  
       2020-03-31 11:11:05 +08:00   ❤️ 1
    没办法,你怎么用小数表示 1/3
    hbolive
        10
    hbolive  
       2020-03-31 11:12:07 +08:00   ❤️ 1
    @mw717if 2 楼是这类情况的通用做法,可能你不太了解。。
    Kakus
        11
    Kakus  
       2020-03-31 11:12:15 +08:00   ❤️ 1
    “100 减去前面总和” 要注意前面数值不能四舍五入,不然会出现最后一个比例为负值的情况。前面要用去尾方式处理。
    Kakus
        12
    Kakus  
       2020-03-31 11:15:27 +08:00   ❤️ 2
    如果数据个数比较多,去尾累积误差就比较大,此时可以换成四舍五入,然后找出最大的那一条数据,误差都给他
    Or2
        13
    Or2  
       2020-03-31 11:16:36 +08:00   ❤️ 1
    Decimal(str)应该可以,Decimal(float) Decimal(double)不可以
    huigeer
        14
    huigeer  
       2020-03-31 11:16:51 +08:00   ❤️ 1
    2 楼真相
    sumahe
        15
    sumahe  
       2020-03-31 11:18:40 +08:00   ❤️ 1
    @littleylv 牛逼
    Jooooooooo
        16
    Jooooooooo  
       2020-03-31 11:24:47 +08:00   ❤️ 1
    相减是对的
    bitdepth
        17
    bitdepth  
       2020-03-31 11:34:35 +08:00 via iPad   ❤️ 1
    from decimal import *
    IEEE 754 了解一下
    niubee1
        18
    niubee1  
       2020-03-31 11:42:53 +08:00   ❤️ 1
    基本的数学原理,分数表示的无理数没有办法用有限的小数位表示,只能近似约等,也就是丢失精度,这是初中数学学的。
    melonrice
        19
    melonrice  
       2020-03-31 11:45:38 +08:00   ❤️ 1
    @niubee1 不是小学吗.....
    ColinDowney
        20
    ColinDowney  
       2020-03-31 11:59:56 +08:00 via Android   ❤️ 1
    2 楼精彩
    ClericPy
        21
    ClericPy  
       2020-03-31 12:14:01 +08:00   ❤️ 1
    用计算机算小数还要那么精确... 转 Fraction 或者小数同乘 1000 转整试试?
    no1xsyzy
        22
    no1xsyzy  
       2020-03-31 12:28:54 +08:00   ❤️ 1
    @niubee1 #18 分数表示的无理数?分数不都是有理数吗?
    承认吧,你数学也不行(
    niubee1
        23
    niubee1  
       2020-03-31 12:35:26 +08:00   ❤️ 1
    @no1xsyzy 被发现了........ 初中以后的数学都还给老师了
    vyronlee
        24
    vyronlee  
       2020-03-31 12:39:49 +08:00 via iPhone   ❤️ 1
    @no1xsyzy 哈哈哈,人艰不拆
    no1xsyzy
        25
    no1xsyzy  
       2020-03-31 12:54:29 +08:00   ❤️ 2
    方便方法就是 #2 补余,但要注意小心 #11 提到的情况,比如 100.0/94=1.1,1.1*93=102.3,最后一个必须是 (-2.3) 才行。
    但如 #12 所说去尾的话也有问题,去尾得到 1.0,1.0*93=93.0,最后一个必须是 7.0

    更好的方法是抖动下降
    a/b = x*10^-y + z/b,其中 a,b,x,y,z∈Z ∧ 0≤z*10^y < b
    那么最终结果
    有 z/b 的概率是 (x+1)*10^-y (余偏 1-z/b )
    有 1-z/b 的概率是 x*10^-y (余偏 z/b )
    将余偏加到下个数上去。
    zane1994
        26
    zane1994  
       2020-03-31 13:07:33 +08:00   ❤️ 1
    使用四舍六入五成双,可以解决一部分
    mw717if
        27
    mw717if  
    OP
       2020-03-31 13:33:20 +08:00
    感谢各位大佬,不一一 @骚扰了,最后用二楼方法解决问题,自己图样,还是要多学习
    no1xsyzy
        28
    no1xsyzy  
       2020-03-31 14:37:10 +08:00   ❤️ 1
    几个方法的比较,采用 943 个 1,即每个 0.106 左右,取 0.01 精确度恰好提升
    默认方法最后会凭空多出 3.7%
    可以看到 rest2last 采用 round even (四舍六入五凑偶)就会导致最后一个是负的
    而如果采用 rest2max 则会导致其中一个过分地大
    而只有 dipping 保证够稳定
    https://gist.github.com/no1xsyzy/088c44304d4186e0bea5704b0b8c2e66
    hxtheone
        29
    hxtheone  
       2020-03-31 15:30:13 +08:00
    @littleylv #2 逻辑鬼才, 捡到宝了
    NeinChn
        30
    NeinChn  
       2020-03-31 17:15:09 +08:00
    @no1xsyzy
    这种方式的术语叫什么名字,我搜了一下 dipping 没有找到类似的解释
    no1xsyzy
        31
    no1xsyzy  
       2020-03-31 17:56:17 +08:00
    @NeinChn #30 是我记错英文名了
    应该叫 dithering,抖动,处理的就是非精确数字化导致的偏差,将比单位还小的量随机地抖动到附近的值上去
    NeinChn
        32
    NeinChn  
       2020-03-31 18:00:14 +08:00
    @no1xsyzy 了解了
    不知道在金融场景上是怎么处理这种情况的
    没做过这方面的考虑的确实比较少
    no1xsyzy
        33
    no1xsyzy  
       2020-03-31 18:36:11 +08:00
    @NeinChn #32 我这算是 DSP 的,提供的是一种舒服的心理感觉,大概从这个了解到的:
    https://xiph.org/video/vid2.shtml (章节 “dither”)

    金融场景倒是不知道,话说算比例的话我隐约感觉看到过总比例保留 99.99%,而且直接在 “总计” 行 “比例(%)” 列写着 “99.99”
    反正是不可能用抖动这种随机办法的。实际上我说的 “保证够稳定” 是针对 “应对不同数据集能够保证比例值的相对有效性”。但本身因为用了随机所以同样数据集每次结果都不一样(除非被 srand(同一个值))。
    NeinChn
        34
    NeinChn  
       2020-03-31 18:58:35 +08:00
    @no1xsyzy 嗯,感觉输入输出必须得稳定才行.不然有 diff 就容易出问题
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2709 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 14:39 · PVG 22:39 · LAX 06:39 · JFK 09:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.