为什么 Python 对这个和进行四舍五入?

Why does Python round this sum?

我正在使用 IEEE 754 定义的 64 位浮点运算。最小的次正规数是:
2^-1074 = 5e-324 = 5 * 10^-16 * 10^-308

将后者添加到 realmin 结果:
2^-1022 + 2^-1074 = 2.2250738585072014 * 10^-308 + 5 * 10^-16 * 10^-308 = (2.2250738585072014 + 0.0000000000000005) * 10^-308 = 2.2250738585072019 * 10^-308

在 Python 中执行加法时,结果略有不同。这是简单的脚本:

import numpy as np

realmin = np.power(2.0, -1022)
print( "realmin\t\t" + str(realmin) )

smallestSub = np.power(2.0, -1074)
print( "smallest sub\t" + str(smallestSub) )

realminSucc = realmin + smallestSub
print( "sum\t\t" + str(realminSucc) )

输出为:

realmin         2.2250738585072014e-308
smallest sub    5e-324
sum             2.225073858507202e-308

为什么要四舍五入?如 realmin 输出所示,有一个额外数字的 space。

Python 对浮点行为并不严格,因此以下部分是推测性的——这取决于实现。

Java 和 Java 脚本要求将浮点值默认转换为字符串,以使用足够的十进制数字来唯一区分浮点值。例如,如果某些浮点格式的可表示值是 3、3.0625、3.125、3.1875 等,则将 3.0625 转换为字符串会产生“3.06”,因为它唯一地将它与 3 和 3.125 区分开来,并且它必须是这么长是因为较短的“3.1”与 3.125 没有区别。但是将 3.125 转换为字符串会产生“3.1”,因为这已经足够了;将 3.1 转换为最接近的可表示值得到 3.125.

因为 Java 和 JavaScript 需要这个,进行这些转换的子例程变得很普遍,并且 Python 实现可能会使用它们,因为它们很容易获得。此行为可以解释您在 Python 实施中看到的结果。

虽然题中写着“2^-1074 = 5e-24”,但事实并非如此。 2−1074 is exactly 4.940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625 • 10−324.浮点数的确切值在格式中很重要。在2−1022附近,可表示的值为:

  • 2−1022 − 2−1074 = 2.2250738585072008 • 10−308.
  • 2−1022 = 2.225073858507201383090232717332404064219215980462331830553327416887204434813918195854283159012511020564067339731035811005152434161553460108856012385377718821130777993532002330479610147442583636071921565046942503734208375250806650616658158948720491179968591639648500635908770118304874799780887753749949451580451605050915399856582470818645113537935804992115981085766051992433352114352390148795699609591288891602992641511063466313393663477586513029371762047325631781485664350872122828637642044846811407613911477062801689853244110024161447421618567166150540154285084716752901903161322778896729707373123334086988983175067838846926092773977972858659654941091369095406136467568702398678315290680984617210924625396728515625 • 10−308.
  • 2−1022 + 2−1074 = 2.2250738585072018 • 10−308.
  • 2−1022 + 2•2−1074 = 2.2250738585072023 • 10−308.

现在我们可以明白为什么2−1022必须显示为“2.2250738585072014e-308”了。如果显示的数字少一位,如“2.225073858507201e-308”,那将比 2−1022 − 2−1074 更接近到2−1022,所以是错误的。

然而,对于 2−1022 + 2−1074,“2.225073858507202e-308”就足够了,因为最接近的可表示值是 2−1022 + 2−1074。 2−1022 + 2•2−1074更远.