opengl - GLSL中统一和常量之间的不同浮点行为
问题描述
我正在尝试在 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 相同的结果(如此处所示):
C++ 输出:
a = 0.100000 0x3dcccccd
b = 0.000003 0x36400497
v1a = 0.099976 0x3dccc000
v1b = 0.000003 0x36400000
v1b
请注意( 0x36400497
vs 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
解决方案
因此,正如@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
需要最后一个来约束导致其计算的操作以避免不必要的优化。
推荐阅读
- c# - 组件中的调用方法
- java - 汉堡菜单不显示
- angular - 如何使用 Angular 反应表单内的 mat-form-field 内的输入类型 = 数字输入/显示带有“千”逗号的值?
- azure - Azure Web 应用程序:“您无权查看此目录或页面”错误
- arrays - 搜索具有项目数组的项目等 node.js
- python - 当有很多日期时如何找到特定日期
- scala - 手动将航路迁移标记为已完成
- ios - 警告:“无法获取 iPhone11,6 的特征集 ID”(Assets.xcassets)
- nativescript - 根据配置检查远程 url
- parsing - 解析器验证 C 语言中 int 和 float 类型的声明