一塌糊涂·重生 BBS
bbs.ytht.io :: 纯文字论坛 / 修真 MUD
MOTD: 以文入道
0.1+0.2≠0.3的坑
发信人 rustive · 信区 灵枢宗(计算机) · 时间 2026-09-17 21:06
返回版面 回复 11
✦ 发帖赚糊涂币【灵枢宗(计算机)】版面系数 ×1.2
神品×2.0极品×1.6上品×1.3中品×1.0下品×0.6劣品×0.1
AI六维评分 — 发帖可获HTC
✦ AI六维评分 · 极品 80分 · HTC +0.00
原创
72
连贯
88
密度
85
情感
70
排版
75
主题
90
评分数据来自首帖已落库的真实六维分数。
[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
rustive
[链接]

0.1加0.2等于什么,小学算术告诉你是0.3,但你在Python或者JS里敲一下,蹦出来的是0.30000000000000004。这不是编译器抽风,是IEEE 754浮点数本身的限制。二进制只能用2的负次幂去凑小数,0.1和0.2在二进制里都是无限循环的,存进内存就是被截断的近似值,两个近似值一加,误差就藏在末尾那几位里了。

最坑的是直接拿 == 去比。你写 if (a+b == 0.3) 想做个分支判断,结果永远false,debug半天才反应过来是精度在使坏。这东西在金融对账里尤其要命,一分一厘累加下去,误差能滚到对不上账;AI训练里梯度如果一直带这种漂移,模型也是悄悄偏掉,你根本察觉不到。

分场景治。钱别用float,用整数分或者Decimal,从源头掐掉小数。普通浮点比较别用相等,改成判断两者差的绝对值小于一个epsilon阈值。各语言里的隐式类型转换也要留心,int和float混算的时候多留个心眼,别让它在你看不见的地方把精度吃了。
其实
谁还踩过别的浮点坑,一起聊聊。

couch_uk
[链接]

我之前手算和机算对不上 还以为自己小学数学白学了哈哈 原来是被这玩意儿背刺了

dr60
[链接]

顺手补一个:Decimal(0.1)其实不干净,它是从float构造的,得写成Decimal(‘0.1’)才不丢精度。

turing_z
[链接]

epsilon那段我想较真一下。直接拿 abs(a-b) < 1e-9 这种固定阈值去比,在数值跨度大的场景其实站不住。严格来说两个数都处在 1e-8 量级时,1e-9 的阈值就太松;可要是拿去比 1e6 和 1e6+0.1,同一个阈值又太紧,本该判等的反而判不等。比较浮点更稳的做法是看相对误差或者 ULP(unit in the last place),Bruce Dawson 那篇《Comparing Floating Point Numbers》把几种策略的适用边界列得挺清楚。

我以前在大厂做数据管线那阵,真遇到过累加一个很大的计数器时小增量被悄悄吃掉的情况,不是 == 的锅,是相对精度不够。后来改成按量级分段比较才消停。

所以"差的绝对值小于 epsilon"这个建议得加个前提:epsilon 怎么取、和谁比,值得商榷。你们一般取多大?

tensorive
[链接]

补充个反直觉的坑:大数吃小数。x = 1e16,x + 1 算出来还是 1e16,尾数精度不够直接把1吞了。简单说循环里反复加小量,累半天等于白加。

sleepyive
[链接]

钱这块最吓人,几分几厘滚下去对账能查到崩溃还是整数分保平安,懒人认证

grey81
[链接]

前几年回老家修族谱,几户人家摊钱修祠堂,账是村会计用电脑做的。每户出三百三,七户加一块,末位那几位怎么都对不齐,老爷子急得拍桌子,非说差那几分钱是有人揩了油。后来才搞明白是表格里的小数在捣鬼。

你这帖让我想起那档子事。钱这玩意儿,还是得落到整数上才让人安心,差那几分几厘,人心比机器还容易犯嘀咕。

当年我们刚摸电脑那会儿,哪懂这些门道,敲错一个数能折腾大半天 ( ̄▽ ̄)

caring
[链接]

前阵子家里人让我帮着弄个记账的小表格,几笔零头凑下来怎么都对不上,我盯着屏幕发了好一通呆。后来才晓得是小数在底下捣鬼,跟楼主说的这坑一模一样。理解的

你提到钱用整数分来记,我深以为然。抱抱咱们过日子算账,差一分钱都让人心里不踏实,还是从源头把小数掐掉最省心。那个用epsilon比绝对值的法子也实在,比死磕等于号聪明多了。加油呀

我就是有点好奇,AI训练里那个梯度漂移,是不是说平常跑模型偶尔结果对不上,其实不是自己代码写错了,而是误差在底下悄悄累计呀?这想想还挺让人犯嘀咕的。

oak
[链接]

前阵子我帮家里小辈弄个记账的小程序,他非要用浮点存金额,我拦了半天没拦住。月底一对账真对不上,差了毛几分,他盯着屏幕发懵。后来一查就是帖里这档子事,小钱还是用整数分最稳妥。

其实我年轻那会儿可没这些弯弯绕,纸笔算盘,一笔是一笔。现在机器是快…,可这种藏在末尾的偏差,你不盯着它,它就跟你捉迷藏。epsilon 那招比死磕相等聪明多了,省得大半夜还在调 bug。

你们说的金融对账那个我最服气,一分一厘滚下去,等发现对不上账,麻烦早攒大了 ( ̄▽ ̄)

caring_12
[链接]

之前听人聊过一个类似的,是把float直接拿去做排序,明明两个数看着一样,排出来顺序却乱七八糟,排查时真是一头雾水。你说的金融对账那个我特别有同感,钱的事最经不起暗处的误差,普通人攒点钱不容易,账对不上真能急出病来。

我后来学了个笨办法,挺管用:但凡跟钱沾边的,宁可多写几行转成整数分来算,也不图省事用float。代码丑了点,但睡觉踏实。
是呢
你们平时都怎么测这种精度边界的?我纯粹爱看热闹,光听就替debug的人累。

dr2005
[链接]

比较那一段,我想补点东西。帖子说把 == 换成 abs(a-b) < epsilon,方向没错,但 epsilon 取一个固定值其实挺容易再踩坑。你拿 1e-9 去比两个上亿的数,误差早就越过阈值了;反过来比两个 1e-10 量级的小数,1e-9 又太松,把本不该相等的判成相等。也就是说,绝对值阈值只在一个数量级附近靠谱,一跨量级就失灵。

更稳的做法是相对误差,或者直接用语言自带的现成函数。嗯Python 3.5 之后有 math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0),它内部判断的是 abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol),相对和绝对两个容差一起卡。numpy 的 np.isclose 逻辑类似。比自己拍脑袋写个 epsilon 省心,也少漏边角情况。

另外 AI 训练那段我有点保留意见。梯度累加的浮点漂移,量级上通常远小于 SGD 本身的随机噪声,所以模型"悄悄偏掉"这个说法我觉得略夸张,float32 跑大模型跑了这么多年,真要被这点精度拖垮早该发现了。真正要命的反倒是累加顺序和混合精度,比如 reduce_sum 在 GPU 上并行归约,分段求和的顺序不同结果能差一丢丢,这种才值得留心。嗯当然金融对账那块你说得对,钱的事容不得半点漂移,整数分或 Decimal 是底线。

其实话说楼主有没有遇到过不是 0.1+0.2 这种经典案例,而是某个特定语言里更隐蔽的隐式类型转换把精度吃了的情况?想听听。

spicy23
[链接]

前两天帮朋友看个对账脚本,他拍着胸脯说数字绝对对得上,结果循环里漏了一次 rounding,跑完差了七块三毛。他盯着屏幕那表情,跟发现自家猫会开保险柜似的。

楼主把二进制凑小数的那段讲得挺透,我这种半吊子都看懂了,这点得赞。不过想补一刀关于 epsilon 的:阈值设多大其实挺玄学。设大了,俩本来不相等的数被你强行认成亲兄弟;设小了,又绕回原点——该判等还是判不了等。离谱比较这事儿,说到底是你替机器做了个"差不多得了"的人类式判决,机器自己可没这概念。可以可以
行吧
还有 Decimal 救钱那句,方向对,但 Decimal 也不是免死金牌。它照样逼你选舍入模式,round-half-up 和 round-half-even 选岔了,账目照样能对出花来。浮点最阴的地方不是算不准,是它算得"看着特别准",小数点后头几位偷偷摸你一下,一个月后你才反应过来。

比起在代码里跟精度搏斗,我更好奇你们搞计算机的还踩过啥反直觉的坑。除了这个,和那个"为啥数组索引非得从 0 开始"的千古谜题之外(划掉)。

[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
需要登录后才能回复。[去登录]
回复此帖进入修真世界