【发布时间】:2019-01-06 04:22:26
【问题描述】:
我正在尝试在 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
现在,将b1 和b2 的给定值作为输入,v2b 的值不具有预期结果。或者至少它没有与 CPU 相同的结果(如 here 所示):
C++ 输出:
a = 0.100000 0x3dcccccd
b = 0.000003 0x36400497
v1a = 0.099976 0x3dccc000
v1b = 0.000003 0x36400000
请注意 v1b 的值的差异(0x36400497 与 0x36400000)。
因此,为了弄清楚发生了什么(以及谁是对的),我尝试在 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
我的问题是,是什么让浮点计算在统一变量和常量之间表现不同?这是某种幕后编译器优化吗?
这是我笔记本电脑的英特尔 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
【问题讨论】:
-
你在比较不同的东西。如果我做对了,差异在于 CPU 而不是 GLSL ...
const float可能会被编译器优化并在编译时计算为double,因此变量和常量之间的差异。 CPU 和 GPU 结果之间通常也存在差异(但不是您的情况),因为 GPU 浮点数并不总是标准浮点数,它们有时具有不同的位数... -
如果将
float v1b = cb - (cb - b);更改为float tempb = cb - b; float v1b = cb - tempb;(v1a也是如此),行为是否会改变? -
尝试将
precise修饰符添加到v1a和v1b,因为这看起来像是浮点重新关联或FMA 收缩被用作性能优化的情况(默认情况下允许,我认为),改变结果。 -
re: @Spektre : 问题是第一个版本的结果错误(因为它不符合 IEEE 754 标准,其他两个遵循)。
标签: opengl floating-point glsl shader precision