GLSL中的常数和常数之间的浮点行为不同

时间:2019-01-06 04:22:27

标签: opengl floating-point glsl shader precision

我正在尝试在GLSL中实现模拟的双精度,并且观察到一个奇怪的行为差异,导致GLSL中存在细微的浮点错误。

请考虑以下片段着色器,写入4浮点纹理以打印输出。

layout (location = 0) out vec4 Output
uniform float s;
void main()
{
  float a = 0.1f;
  float b = s;

  const float split = 8193.0; // = 2^13 + 1

  float ca = split * a;
  float cb = split * b;

  float v1a = ca - (ca - a);
  float v1b = cb - (cb - b);

  Output = vec4(a,b,v1a,v1b);
}

这是我观察到的输出

统一的GLSL输出:

a = 0.1            0x3dcccccd
b = 2.86129e-06    0x36400497
v1a = 0.0999756    0x3dccc000
v1b = 2.86129e-06  0x36400497

现在,使用b1b2的给定值作为输入,v2b的值没有预期的结果。或至少它的结果与CPU上的结果不同(如here所示):

C ++输出:

a = 0.100000     0x3dcccccd
b = 0.000003     0x36400497
v1a = 0.099976   0x3dccc000
v1b = 0.000003   0x36400000

请注意v1b值的差异(0x364004970x36400000)。

因此,为了弄清楚正在发生的事情(以及谁是对的),我尝试在GLSL中重做计算,使用稍有修改的着色器将常量替换为常量,然后将常量替换为常量。

layout (location = 0) out vec4 Output
void main()
{
  float a = 0.1f;
  float b = uintBitsToFloat(0x36400497u);

  const float split = 8193.0; // = 2^13 + 1

  float ca = split * a;
  float cb = split * b;

  float v1a = ca - (ca - a);
  float v1b = cb - (cb - b);

  Output = vec4(a,b,v1a,v1b);
}

这次,我得到与相同计算的C ++版本相同的输出。

带有常量的GLSL输出:

a = 0.1            0x3dcccccd
b = 2.86129e-06    0x36400497
v1a = 0.0999756    0x3dccc000
v1b = 2.86102e-06  0x36400000

我的问题是,是什么使浮点计算在均匀变量和常量之间表现出不同?这是幕后的编译器优化吗?

这是笔记本电脑的intel GPU中的OpenGL供应商字符串,但是我在nVidia卡上也观察到了相同的行为。

Renderer : Intel(R) HD Graphics 520
Vendor   : Intel
OpenGL   : 4.5.0 - Build 23.20.16.4973
GLSL     : 4.50 - Build 23.20.16.4973

2 个答案:

答案 0 :(得分:3)

GPU 不一定具有/使用 IEEE 754 。某些实现的位数较少,因此,不费吹灰之力,结果就会有所不同。与您在 FPU 上比较floatdouble的结果相同。但是,如果您的 GLSL 实现允许,请尝试提高精度。

在更坏的情况下,如果您的 GPU 允许使用doubledvec,但是请注意,目前还没有64位插值器(至少据我所知)。

要排除由于纹理传递结果而导致的舍入,请参见:

您还可以仅通过打印来检查 GPU 上的尾数位数

1.0+1.0/2.0
1.0+1.0/4.0
1.0+1.0/8.0
1.0+1.0/16.0
...
1.0+1.0/2.0^i

最后一个未打印为i的数字1.0是尾数位数。因此,您可以检查它是否为23 ...

答案 1 :(得分:1)

因此,如@njuffa所述,在注释中,通过对依赖严格IEEE754操作的值使用 precise 解决了该问题:

layout (location = 0) out vec4 Output
uniform float s;
void main()
{
  float a = 0.1f;
  float b = s;

  const float split = 8193.0; // = 2^13 + 1

  precise float ca = split * a;
  precise float cb = split * b;

  precise float v1a = ca - (ca - a);
  precise float v1b = cb - (cb - b);

  Output = vec4(a,b,v1a,v1b);
}

输出:

a = 0.1            0x3dcccccd
b = 2.86129e-06    0x36400497
v1a = 0.0999756    0x3dccc000
v1b = 2.86102e-06  0x36400000

编辑:很可能只需要最后一个precise来约束导致其计算的操作,以避免不必要的优化。

相关问题