在 HLSL 中进行 64 位加法,为啥我的一种实现会产生不正确的结果?
Posted
技术标签:
【中文标题】在 HLSL 中进行 64 位加法,为啥我的一种实现会产生不正确的结果?【英文标题】:Doing 64bit addition in HLSL, why is one of my implementations producing incorrect results?在 HLSL 中进行 64 位加法,为什么我的一种实现会产生不正确的结果? 【发布时间】:2015-10-18 07:42:39 【问题描述】:我在 HLSL 中有 2 个 64 位添加的不同实现。如果我想设置 A += B,其中 al、ah、bl 和 bh 分别是 A 和 B 的低 32 位和高 32 位,那么我可以这样做
(1):
#define pluseq64(al, ah, bl, bh) do \
uint tadd0 = al >> 1;\
uint tadd1 = bl >> 1;\
tadd0 += al & bl & 0x00000001;\
tadd0 += tadd1;\
tadd0 >>= 31;\
al += bl;\
ah += bh;\
ah += tadd0;
或(2):
#define pluseq64(al, ah, bl, bh) do \
uint t = al;\
al += bl;\
ah += bh;\
if (al < t) \
ah += 1; \
while(0)
现在,有趣的是,(1) 总是产生正确的输出,而 (2) 不会。鉴于(1)是一种混乱的操作(3个班次,5个加法来做一个64位+=),我更喜欢沿着(2)到(1)的路线,除了(2)不能正常工作。
作为 (2) 的替代方法,我尝试过:
#define pluseq64(al, ah, bl, bh) do \
uint t = al;\
al += bl;\
ah += bh;\
ah += (al < t); while(0)
这也不太有效(可能出于相同的原因,不管那个原因是什么,如果我有我的猜测的话)。
为什么 (2) 不能正常工作?奖励:有没有更好的方法在 HLSL 中添加 64 位?
谢谢!
【问题讨论】:
出错的输入是什么? 我可能需要一些时间才能准确找到它偏离的位置(GPU 内部没有断点功能,等等)。有趣的是,当我放入一些存在进位位的测试用例时,两个版本都产生了正确的输出......但问题仍然是,当我使用版本 1 和版本 2 时,累积输出是不同的。就是这样奇怪。 【参考方案1】:在我的测试中,这三个似乎在 C++ 上产生了相同的输出,所以这有点奇怪。您是否进行了 CPU 端测试并在那里为您工作?您可以尝试的一件事是跳过宏 & do/while 的东西,看看它是否适用于一个简单的 HLSL 函数:
void pluseq64(inout uint al, inout uint ah, in bl, in bh)
uint t = al;
al += bl;
ah += bh;
if (al < t)
ah += 1;
// or "ah += uint(al < t);
无论如何,函数在 HLSL 中是内联的,所以我认为您不会从使用预处理器指令中获得任何好处。
【讨论】:
【参考方案2】:也许您的 sn-p 显示了一个较旧的驱动程序错误?使用PIX 逐步完成反汇编可能会有所帮助。我在 Nvidia/AMD/Intel 上使用了以下没有问题,基本上等同于您的 (1)。
struct uint64_emulated
uint32_t low;
uint32_t high;
inline uint64_emulated Add(uint64_emulated a, uint64_emulated b)
uint64_emulated c;
c.low = a.low + b.low;
c.high = a.high + b.high + (c.low < a.low); // Add with carry.
return c;
【讨论】:
以上是关于在 HLSL 中进行 64 位加法,为啥我的一种实现会产生不正确的结果?的主要内容,如果未能解决你的问题,请参考以下文章