为什么我在 GPU 上有很大的精度错误?

Why do I have big precision errors on GPU?

我正在 GPU 上进行一系列计算,需要足够好的精度,但我得到的精度似乎比在 CPU.

上使用 float 时要低得多

对于初学者来说,当我在浮点缓冲区中加载 0.01 的值时,它会在着色器中加载为 0.009995。这是为什么?我认为 0.01 是浮点向量范围内的值(使用可用于 Metal 的 simd 库)。

然后,当做这样一个简单的操作时,精度明显变差:

simd::float4 p = simd::float4 { -0.04, -0.07, 0, 1 };
simd::float4 v = myMatrix * p;
v *= 1.0 / v.w;
示例中的

p 是我在 CPU 测试中期望和使用的;在 GPU 上它被计算为 { -0.039978, -0.069946, 0.0, 1.0 },用一个整数减法和一个浮点数乘以已经错误的 0.009995.

我期望从 v 得到的是 { -0.010627, 0.006991, -0.034100 }(使用 CPU 上的 simd 库计算,精度已经比使用双精度更差,{ -0.010613, 0.006982, -0.034056 },但可以忍受)。

我得到的是 { -0.010483, 0.006405, -0.044067 }。随着后续操作的进行,情况变得更糟,结果很快变得无法使用。

为什么即使使用相同的精度结果也如此不同,为什么没有加载浮点数据1:1?我尝试禁用 Metal 的 fast math 选项,但它没有改变任何东西。

浮点值不能有 0.01,因为没有二进制表示。这就是使用 0.009995 的原因。我想 SO 已经有了关于二进制浮点数表示的很好的答案,所以你只需要搜索。

这里 good tool 用于检查浮点数在二进制中的样子。如果你输入 0.01 你会看到这个:

Decimal Representation: 0.01
Binary Representation: 00111100001000111101011100001010
After casting to double precision: 0.009999999776482582

唉,这不是精度问题,因为我设置测试的方式不正确,所以 GPU 没有使用我实际认为它使用的数字。