IEEE754浮点数1/x * x > 1.0。

5
我想知道在以下假设下,下面定义的程序是否可以返回1:
  • 使用 IEEE754 浮点运算
  • 没有溢出(在 max/x 和 f*x 中都没有)
  • 没有 NaN 或 Inf(显然)
  • 0 < x 且 0 < n < 32
  • 没有不安全的数学优化
int canfail(int n, double x) {
    double max = 1ULL << n; // 2^n
    double f = max / x;
    return f * x > max;
}

在我看来,有时应该返回1,因为通常情况下roundToNearest(max / x)可能大于max/x。我能找到相反情况的数字,其中f * x < max,但我没有例子表明f * x > max,也不知道如何找到一个。有人可以帮忙吗?
编辑: 我知道x的值在10^(-6)和10^6之间(这仍然留下了很多(太多)可能的双倍精度值,但我知道我不会遇到溢出、下溢或次正常数!此外,我刚刚意识到,由于max是2的幂,并且我们不涉及溢出,所以通过固定max=1,解决方案将完全相同,但偏移了。
因此,问题对应于找到一个正常的双倍精度值x,使得`(1/x)*x>1.0`!
我写了一个小程序来尝试找到解决方案:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
#include <omp.h>

int main( void ) {
    #pragma omp parallel
    {
        unsigned short int xsubi[3] = {
            omp_get_thread_num(),
            omp_get_thread_num(),
            omp_get_thread_num()
        };

        #pragma omp for
        for(int64_t i=0; i<INT64_MAX; i++) {
            double x = fmod(nrand48(xsubi), 1048576.0);
            if(x<0.000001)
                continue;

            double f = 1.0 / x;
            if(f * x > 1.0) {
                printf("found !!! x=%.30f\n", x);
                fflush(stdout);
            }
        }
    }
    return 1;
}

如果您改变比较的符号,您将很快找到一些值。然而,使用 f * x > 1.0 看起来似乎永远运行下去。


2
虽然您提到 1 << n 不会溢出,但是您应该确保使用足够大的无符号类型来适应可能的位数,例如 1ull << n - user694733
我在n的限制条件上提供了更详细的说明,并添加了“ull”。 - yakoudbz
f允许成为次正规数吗? - alias
实际上,x是我的应用程序中的几何坐标。f是次正规的意思是x异常地大... - yakoudbz
非常正确,但这并没有回答我的问题! - alias
显示剩余3条评论
3个回答

4
在不存在下溢或上溢的情况下,指数是无关紧要的;如果 M/x*x > M,那么对于任何二的幂次方 p 和 q,(M/p) / (x/q) * (x/q) > (M/p)。因此,让我们考虑 252x < 253M = 2105。我们可以消除 x = 252,因为这会产生精确的浮点算术,所以 252 < x < 253
将 2105 除以 x 得到整数商 q 和余数 r,其中 252 q < 253,0 < r < x,且 2105 = qx + r
为了使 M/x*x 超过 M,除法和乘法都必须四舍五入。由于除法向上取整,x/2 ≤ r
向上取整,浮点数除法的结果为 2105 除以 x 的商为 q+1。那么精确(非四舍五入)乘法得到 (q+1)•x = qx + x = qx + x + r - r = qx + r + xr = 2105 + xr。由于 x/2 < r,所以 xrx/2,因此将此精确结果四舍五入将向下舍入,得到 2105。(“<” 情况总是向下舍入,“=” 情况向下舍入,因为 2105 具有低偶数位。)
因此,在指数范围内的所有算术和二的幂次方 M 中,使用 round-to-nearest-ties-to-even,M/x*x > M 永远不会发生。

1
参见:Alan Edelman,“当x✱(1 / x)≠1时是什么?” [在线草稿](http://www-math.mit.edu/~edelman/homepage/papers/ieee.pdf),1994年12月7日。 - njuffa

1

2的幂次方乘法只是指数的缩放,它不会改变问题:因此与找到 x 使得 (1/x) * x > 1 是相同的。

一种解决方案是暴力搜索。
出于同样的原因,我们可以将搜索这样的 x 的范围限制在区间 (1.0,2.0( 内。

更好的方法是分析误差边界而不使用暴力搜索。

让我们将 ix 表示为最接近 1/x 的浮点数。

考虑到xix都是精确分数,我们可以写出整数除法:1 = ix * x + r,其中r是余数
(这些都是分母为2的幂的分数,因此我们必须将整个方程乘以适当的2的幂才能真正进行整数除法)。
换句话说,ix = 1/x - r/x,其中-r/x是倒数的舍入误差。
当我们将逆近似值乘以x时,确切值为ix*x = 1 - r
我们知道,浮点结果将四舍五入为最接近该确切值的浮点数。
因此,假设默认舍入方式为最接近、平均值向偶数舍入,则问题所问的是-r是否可能超过0.5 ulp
简短的答案是永远不会!假设|r| > 0.5 ulp,则舍入误差-r/x确实超过了精确结果1/x的半个ulp。这不是一个恰当的答案,因为精确结果不是浮点数,也没有ulp,但你可以理解这个想法...如果我有时间,我可能会回来证明正确性,但我打赌你已经可以找到它了,可能在SO上。
编辑:为什么可以找到(1/x) * x < 1?因为1.0处于二进制限制,所以下面我们必须证明r < 0.25 ulp,但这是无法实现的...

我去看了我的同事,我们恰好得出了相同的答案!刚回到办公室发现了你的回复。 - yakoudbz
"将最接近1/x的浮点数四舍五入是一个不错的第一步。然而,IEEE754标准中的舍入模式可能与最接近模式不同。我怀疑这个答案在其他模式下仍然适用,但值得调查是否存在反例。" - chux - Reinstate Monica
@chux-ReinstateMonica 对,我本来可以提到默认的舍入模式... - aka.nice
@chux:当四舍五入向上(朝+无穷大)时,它当然无法保持。精确的除法将会向上舍入,然后乘积必然超过原始被除数。 - Eric Postpischil
@yakoudbz 还要注意,我假定采用严格的IEEE754标准,因此您必须使用适当的C编译器选项来提供这种保证(不使用过度精度等...) - aka.nice
显示剩余3条评论

1

canfail(1, pow(2, 1023) * (2 - pow(2, -51)))将返回1。


179769313486231550856124328384506240234343437157459335924404872448581845754556114388470639943126220321960804027157371570809852884964511743044087662767600909594331927728237078876188760579532563768698654064825262115771015791463983014857704008123419459386245141723703148097529108423358883457665451722744025579520 绝对不在我认为的数字范围内... - yakoudbz
请提供有关您问题的更多详细信息。 - Dimas Mendes
为什么?这个值可以用IEEE754双精度格式表示。 - kashi
1.0/(pow(2, 1023) * (2 - pow(2, -51))) = 5.56268464626800345772558179333e-309这绝对是一个次正常数,因为最小的正常数大约是1e-38。我想要一个只涉及正常数且没有溢出或下溢的证明。我应该更加精确。无论如何,我得到了两个令人满意的答案,严格证明了在IEEE754舍入至最近偶数时不可能发生。 - yakoudbz
好的。我明白了。但是,双精度浮点数的最小正常数是2^(-1022)约等于2.225e-308。单精度浮点数的最小正常数是1e-38。就像你所说的,5.56e-309是一个次正常数,我认为这种现象不会发生在正常数中。 - kashi
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接