【问题标题】:Does "0.00x <= 0.001 * x" always hold for double?“0.00x <= 0.001 * x”是否总是双倍?
【发布时间】:2019-07-30 08:26:19
【问题描述】:

例子:

0.008 == 0.001 * 8
0.009  < 0.001 * 9
0.035 == 0.001 * 35
0.036  < 0.001 * 36

我还测试了以下模式:

0.0x   <= 0.01   * x
0.000x <= 0.0001 * x

我已经在 MATLAB 和 C 中测试了数千个。

我的问题:

  1. 如果它总是成立,为什么?如果没有,有反例吗?

  2. 如果它总是成立,那么下面的所有模式都正确吗?

    0.0000000000000x &lt;= 0.0000000000001 * x ; for arbitrary zeros

【问题讨论】:

  • 显然这是错误的for arbitrary zeros,因为浮点类型没有无限精度。放置足够多的零和 0.000....01*x 将导致 0
  • 你改变了舍入模式吗?

标签: floating-point double ieee-754


【解决方案1】:

.000005 &lt;= .000001 * 5 计算结果为假。

Matlab specifies double to be IEEE-754 binary64,但 C 标准没有,尽管许多实现都使用它。通常,基数 2 和 10 用于浮点数。在以十为底的情况下,提出的关系成立,实际上这两个表达式在格式的范围内是相等的。 (当x 太大以至于将其转换为double 会产生无穷大时,等式不成立。)在其他基础中,可以找到与此答案中所示类似的反例。1 对于这个答案的其余部分,假定 IEEE-754 binary64,无论是数字格式还是操作行为。

我们应该了解.000…000x &lt;= .000…0001 * x 这样的表达式是如何计算的:

  • 首先,源文本中的每个数字都转换为double。在此转换过程中,数字会四舍五入为可表示的值。这种舍入通常通过舍入到最接近的可表示值来完成,并与偶数低位(位)的值相关联。
  • 然后进行乘法运算,结果就像将实数结果四舍五入到可表示的值一样。同样,四舍五入很常见。
  • 然后评估比较&lt;=。这没有错误;当且仅当右操作数大于或等于左操作数时,它才会产生 true。

我假设x 是非负数。对于否定的x,请参见第一个附录。

首先,考虑使用四舍五入。

.000001 转换为double 得到0.0000009999999999999999954748111825886258685613938723690807819366455078125。这可以在 printf(".99f\n", .000001); 的良好 C 实现中看到。 (由于 C 标准既没有完全指定编译时将十进制数字转换为浮点数,也没有通过printf 将浮点数转换为十进制数,因此在不使用正确舍入到最近的实现中可能存在差异。 ) 正如我们所见,这小于 0.000001。这很容易通过打印.1.01.001 等找到,直到我们找到一个恰好向下取整的数字。然后我们测试各种x,直到我们找到.00000x 碰巧四舍五入的一个。将.000005 转换为double 得到0.0000050000000000000004090152695701565477293115691281855106353759765625。接下来,对于小整数xx 完全可以在double 中表示,因此将该操作数转换为double 没有舍入错误。我们几乎满意.00000x &lt;= .000001 * x,因为左侧包含向上舍入,而右侧包含向下舍入。然而,乘法也可能有一个舍入,这会破坏我们的准备。同样,测试几个x 可能会找到一个不会发生这种情况的例子——如果乘法不精确,那么它是否恰好向上或向下取整取决于x 中的某个位,所以很少的试验足以找到一个可行的例子。

回到原来的比例,0.001 而不是 .000001,我们可以说 .00x &lt;= .001 * x 适用于所有小于 253x。这是因为所有这些整数 x 都可以在 double 中精确表示,并且 .001double 的转换向上舍入,产生 0.001000000000000000020816681711721685132943093737570288089。因此,即使左侧 .00x 向下舍入,右侧也包含只能通过向下舍入来补偿的向上舍入,因为在 x 到 @ 的转换中没有舍入987654357@。由于每个舍入只能移动到下一个可表示的值,而不是跳过任何一个,因此乘法中的舍入不能使右侧低于左侧。所以.00x &lt;= .001 * x 适用于所有小于 253x

以上,将x转换为double可能会导致舍入错误,搜索容易找到反例(在253以上的第四个奇数):9007199254741.001 &lt;= .001 * 9007199254741001,对于将9007199254741.001 转换为double 生成9007199254741.001953125,将9007199254741001 转换为double 生成9007199254741000,右侧计算为9007199254741

如果我们考虑其他舍入模式:

  • 随着向 -∞(向下四舍五入)或向 0 舍入,右侧将遭受最多三个向下舍入错误,因此我们可以预料到关系失败的许多情况。
  • 随着向 +∞ 舍入(向上舍入),右侧必须遇到至少一个向上舍入错误,因为 .000…0001 永远不能精确地表示为以 2 为底的浮点数,并且向上舍入规则不允许右侧可以消除此错误,左侧只能有一个舍入误差,永远不会超过右侧的误差,因此关系必须始终成立。

附录

x 可以是负数吗?该问题的语法表明不,因为x = -3,.00x 将变为.00-3,这不会形成正确的数字。如果我们允许它为-.003,那么当x 的幅度如此之大以至于将其转换为double 会产生-∞ 时,.00x &lt;= .001 * x 会失败,但不会那么大以至于将.00x 转换为double 会产生- ∞。在这种情况下,我们将一个有限值与 -∞ 进行比较,比较结果为假。在double 格式的范围内,值仍然是有限的,比较会遇到上面讨论的问题,但需要对舍入规则进行一些修改。

请注意,如果.000…0001 足够小,则将其转换为double 可能会产生零(在任何标准舍入模式下,而不是朝向+∞),并且x 可能足够大以至于它产生∞,其中将它们相乘会产生 NaN(或陷阱),并且这种关系不成立,因为 NaN 与数字无关。

脚注

1在十的倍数的基数中,可能存在一些此答案未探索的异常相互作用。

【讨论】:

    【解决方案2】:

    这不是你问的,因为0.00x 不是数学运算。但也许考虑以下相关问题会很有趣:

    x 是IEEE754 双精度数时,x / 100 == 0.01 * x 是真的吗?

    即使忽略NaN 的明显情况,这种相等也不成立。也就是说:

    Prelude> 0.9033208460939007 / 100
    9.033208460939007e-3
    Prelude> 0.9033208460939007 * 0.01
    9.033208460939008e-3
    

    (我使用了 Haskell/ghci 提示符,但您可以在任何执行 IEEE754 二进制浮点运算的语言中复制它,这几乎是所有语言。)

    注意最后一位的结果有何不同。

    不用说,对于四舍五入和有限精度,几乎没有“明显的”相等性适用于浮点数。从某种意义上说,这是设计使然。浮点数是一种表示“实数”的方法,其存储量有限,足以使用,但不应用于代数运算:例如,+* 等的关联性是一个经典案例它只是不适用于浮点数,但您会期望它来自任何“数学”数字概念。

    【讨论】:

    • OP 没有询问是否相等,他们知道它不成立。他们在问它是否总是相等或更小,或者有时会更大。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-27
    • 1970-01-01
    • 1970-01-01
    • 2011-09-08
    • 1970-01-01
    • 2017-05-09
    • 1970-01-01
    相关资源
    最近更新 更多