为什么 4*0.1 的浮点值在 Python 3 中看起来不错,而 3*0.1 却不好看?

2025-04-16 08:56:00
admin
原创
15
摘要:问题描述:我知道大多数小数没有精确的浮点表示(浮点数学有问题吗?)。但我不明白为什么4*0.1打印出来很漂亮0.4,但3*0.1事实并非如此,因为两个值实际上都有丑陋的十进制表示:>>> 3*0.1 0.30000000000000004 >>> 4*0.1 0.4 >...

问题描述:

我知道大多数小数没有精确的浮点表示(浮点数学有问题吗?)。

但我不明白为什么4*0.1打印出来很漂亮0.4,但3*0.1事实并非如此,因为两个值实际上都有丑陋的十进制表示:

>>> 3*0.1
0.30000000000000004
>>> 4*0.1
0.4
>>> from decimal import Decimal
>>> Decimal(3*0.1)
Decimal('0.3000000000000000444089209850062616169452667236328125')
>>> Decimal(4*0.1)
Decimal('0.40000000000000002220446049250313080847263336181640625')

解决方案 1:

简单的答案是由于3*0.1 != 0.3量化(舍入)误差(而4*0.1 == 0.4乘以2的幂通常是“精确”运算)。Python 会尝试找到能够舍入到所需值的最短字符串,因此它可以显示4*0.10.4“因为这些相等”,但不能显示3*0.1为“0.3因为这些不相等”。

您可以使用.hexPython 中的方法来查看数字的内部表示(基本上是精确的二进制浮点值,而不是十进制的近似值)。这有助于解释底层发生的事情。

>>> (0.1).hex()
'0x1.999999999999ap-4'
>>> (0.3).hex()
'0x1.3333333333333p-2'
>>> (0.1*3).hex()
'0x1.3333333333334p-2'
>>> (0.4).hex()
'0x1.999999999999ap-2'
>>> (0.1*4).hex()
'0x1.999999999999ap-2'

0.1 等于 0x1.999999999999a 乘以 2^-4。末尾的“a”表示数字 10——换句话说,二进制浮点数 0.1比“精确”值 0.1略大(因为最终的 0x0.99 被四舍五入为 0x0.a)。当将其乘以 4(2 的幂)时,指数会向上移动(从 2^-4 变为 2^-2),但其他数字保持不变,因此4*0.1 == 0.4

然而,当你乘以 3 时,0x0.99 和 0x0.a0(0x0.07)之间的微小差异会放大为 0x0.15 的错误,并在最后一位显示为一位数错误。这导致 0.13 比四舍五入后的数值 0.3 略大。*

Python 3 的浮点数repr被设计为可转换的,也就是说,显示的值应该能够精确地转换为原始值(float(repr(f)) == f对于所有浮点数而言f)。因此,它无法以完全相同的方式显示0.3和,否则两个不同的数字在转换后会变成相同的结果。因此,Python 3 的引擎选择显示一个略带明显错误的值。0.1*3`repr`

解决方案 2:

repr(在 Python 3 中也是如此str)会根据需要输出尽可能多的数字,以确保值无歧义。在本例中,乘法的结果3*0.1不是最接近 0.3 的值(十六进制为 0x1.333333333333p-2),实际上它比 0.3 低一位(0x1.333333333334p-2),因此需要更多数字才能将其与 0.3 区分开来。

另一方面,乘法4*0.1 确实得到了最接近 0.4 的值(十六进制为 0x1.999999999999ap-2),因此不需要任何额外的数字。

你可以很容易地验证这一点:

>>> 3*0.1 == 0.3
False
>>> 4*0.1 == 0.4
True

上面我用的是十六进制表示法,因为它简洁美观,并且能够清晰地显示两个值之间的位差。你可以自己用例如 来做(3*0.1).hex()。如果你想看完整的十进制表示法,可以参考以下代码:

>>> Decimal(3*0.1)
Decimal('0.3000000000000000444089209850062616169452667236328125')
>>> Decimal(0.3)
Decimal('0.299999999999999988897769753748434595763683319091796875')
>>> Decimal(4*0.1)
Decimal('0.40000000000000002220446049250313080847263336181640625')
>>> Decimal(0.4)
Decimal('0.40000000000000002220446049250313080847263336181640625')

解决方案 3:

这是从其他答案中得出的简化结论。

如果您在 Python 的命令行上检查浮点数或打印它,它会通过repr创建其字符串表示形式的函数。

从 3.2 版开始,Python 的strrepr使用复杂的舍入方案,该方案尽可能采用美观的小数,但在必要时使用更多数字来保证浮点数和它们的字符串表示之间的双射(一对一)映射。

这种方案保证了 的值repr(float(s))对于简单的小数来说看起来很好看,即使它们不能精确地表示为浮点数(例如当 时s = "0.1"))。

同时它保证float(repr(x)) == x每个浮点数都成立x

解决方案 4:

并不是特定于 Python 的实现,但应该适用于任何浮点数到十进制字符串函数。

浮点数本质上是二进制数,但采用科学计数法表示,且有效数字有固定的限制。

任何数的逆,如果其素数因子不与基数共享,其结果总是循环小数点表示。例如,1/7 的素数因子 7 与 10 不共享,因此具有循环小数点表示。1/10 的素数因子为 2 和 5,且后者不与 2 共享,因此也具有循环小数点表示。这意味着 0.1 无法用小数点后有限的位数精确表示。

由于 0.1 没有精确的表示,将近似值转换为小数点字符串的函数通常会尝试近似某些值,以便它们不会得到不直观的结果,如 0.1000000000004121。

由于浮点数采用科学计数法,因此任何乘以基数幂的操作都只会影响数字的指数部分。例如,十进制表示法中 1.231e+2 100 = 1.231e+4;同样,二进制表示法中 1.00101010e11 100 = 1.00101010e101。如果乘以基数的非幂,有效数字也会受到影响。例如,1.2e1 * 3 = 3.6e1

根据所使用的算法,它可能会尝试仅根据有效数字来猜测常见的小数。0.1 和 0.4 在二进制中具有相同的有效数字,因为它们的浮点数本质上分别是 (8/5) (2^-4) 和 (8/5) (2^-6) 的截断。如果算法将 8/5 的有效数字模式识别为小数 1.6,那么它将适用于 0.1、0.2、0.4、0.8 等。它也可能具有其他组合的神奇有效数字模式,例如浮点数 3 除以浮点数 10,以及其他统计上可能由除以 10 形成的神奇模式。

对于 3*0.1 的情况,最后几个有效数字可能与浮点数 3 除以浮点数 10 不同,导致算法无法根据其对精度损失的容忍度识别 0.3 常数的神奇数字。

编辑:
https ://docs.python.org/3.1/tutorial/floatingpoint.html

有趣的是,许多不同的十进制数都拥有相同的最接近的二进制小数近似值。例如,0.1、0.10000000000000001 和 0.1000000000000000055511151231257827021181583404541015625 这几个数字,它们的近似值都是 3602879701896397 / 2 ** 55。由于所有这些十进制值都拥有相同的近似值,因此它们中的任何一个都可以显示,同时仍然保留不变式 eval(repr(x)) == x。

不允许有精度损失,如果 float x (0.3) 不完全等于 float y (0.1*3),那么 repr(x) 也不完全等于 repr(y)。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用