1

我试图更好地理解浮点值在实数线上的分布。

我编写了这段代码来计算范围(-R,R)中均匀分布的可表示值的数量,其中 R 是10的幂(也尝试使用 2 的幂):

public class Foo {
    public static void main(String[] args)
    {
        for(int i=0; i<24; i++)
        {
            int count = 0;
            float R = (float) Math.pow(10, i); //(2<<i);
            float Rstep = Math.ulp(R);
            for(float x = -R; x <= R; x+=Rstep)
                count++;
            System.out.println(R+" "+count+" "+Math.ulp(R));
        }
    }
}

我对结果的差异感到惊讶,即

1.0 16777217 1.1920929E-7
10.0 20971521 9.536743E-7
100.0 26214401 7.6293945E-6
1000.0 32768001 6.1035156E-5
10000.0 20480001 9.765625E-4
100000.0 25600001 0.0078125
1000000.0 32000001 0.0625

正如我半信半疑的那样,均匀分布的值的数量将是 16777216(即 23 位尾数的 1<<23,由于符号位而加倍)。

为了让这个问题更加具体 - 我正在尝试构建一个模型(它使用精确到几个数量级的 SI 单位,例如距离以公里到纳米为单位),但必须将其映射到浮点空间(以加载到 GPU 中)。由于这是一个科学模型,我需要了解精度丢失的地方。计划是将值捕捉到均匀分布的范围 - 因此从上表捕捉到范围 (-1000,1000) 将为我提供 32768001 个精确值。

对我来说,在这些范围内会有如此多的差异,以及为什么 2 个案例的力量有限,这似乎违反直觉。

有谁能够解释如何思考这个问题?

干杯

4

1 回答 1

2

你真的应该用十六进制打印浮点数,那样会更清楚。

您对“16777216(即 23 位尾数的 1<<23,由于符号位而加倍)”的估计仅是您预期的一半。最好的情况是从一个看起来像 -0x1.FFF...pX 的十六进制数开始,即正好是 2 次方以下的数字的相反数。当重复添加初始 ULP 时,您确实会使用指数 X 遍历有效数字的所有值。正如您推断的那样,这是 1<<23 步。完成后,您将接近零的一半。相同数量的步数将使您归零(指数低于 X),然后再次将步数加倍以获得正值。

因此,这是 1<<25 (~32000000) 或如此均匀间隔的浮点数,可以在任何接近 2 的幂及其相反数之间找到。你得到 1000 的步数,因为 1000 刚好在两个 1024 的幂次方之下。

正如您所注意到的,最坏的情况是从一个刚好高于 2 的幂的数字开始,比如 0x1.00001pX。然后,您几乎不遍历指数 X 的值,而是立即开始访问具有较低指数的值。如果您从 0x1.FFF...pX 开始,您最终访问的值只有一半


注意:该符号-0x1.123defpX应解释为 -0x1.123def * 2 ^ X。也许您的编程语言接受它作为浮点值的输入和/或输出。要重新迭代,在尝试了解正在发生的事情时非常方便。

于 2013-03-22T22:36:29.467 回答