我试图更好地理解实数行上浮点值的分布。
我编写此代码来计算范围(-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 <&lt;&lt; 23,由于符号位加倍)。
在这个问题背后提出一些具体内容 - 我正在尝试构建一个模型(使用精确到几个数量级的SI单位,例如以千米到纳米的距离),但必须将其映射到浮动空间(用于加载到GPU)。由于这是一个科学模型,我需要了解精度丢失的地方。计划是将值捕捉到均匀分布的范围 - 所以从上面的表中捕捉到范围(-1000,1000)将给出32768001个确切的值。
对我而言,在这些范围内存在如此多的差异,以及为什么2个案例的权力有限,这似乎是违反直觉的。
有人能解释如何考虑这个吗?
欢呼声
答案 0 :(得分:2)
你应该用十六进制打印浮点数,然后会更加清晰。
您对“16777216的估计(即23位尾数的1 <&lt;&lt; 23,由于符号位加倍)”只是您所期望的一半。最好的情况是从一个看起来像十六进制的-0x1.FFF ... pX的数字开始,也就是说,数字恰好低于2的幂。当重复添加初始ULP时,您确实将使用指数X遍历有效数字的所有值。当您推断时,这是1 <&lt;&lt;&lt; 23步。当你完成后,你将一半接近于零。相同数量的步骤将使您为零(指数低于X),然后再将步数加倍为正值。
因此,这是1 <&lt; 25(~32000000)左右的均匀间隔的浮子,可以在任何接近2的功率和其相反的功率之间找到。你得到的步数为1000,因为1000的功率只有两个1024。
最糟糕的情况,就像你注意到的那样,是从一个刚好超过2的幂的数字开始,比如0x1.00001pX。然后,您几乎没有使用指数X遍历值,而是立即开始访问具有较低指数的值。如果你从0x1.FFF ... pX
开始,你最终只能访问一半的值 注意:符号-0x1.123defpX
应解释为-0x1.123def * 2 ^ X.也许您的编程语言接受它来输入和/或输出浮点值。要重新迭代,在尝试理解正在发生的事情时非常方便。