当x1m0 n1,的类型为int
时,powf
会以意外的方式产生奇怪的基数输出。例如,powf(-4,2)
会传回16
,但powf(-5,2)
会传回24
!!!
在一次长时间的计算中追踪了一个错误输出的根之后,我发现当输出类型为integer
时,powf
函数对奇数表现出奇怪的行为。
__global__ void intFoo( int* a)
{
*a = powf(*a, 2);
}
__global__ void doubleFoo( double* a)
{
*a = powf(*a, 2);
}
我可以在Matlab中调用这个内核(例如):
!nvcc -ptx test.cu
k1 = parallel.gpu.CUDAKernel('test.ptx', 'test.cu', 'intFoo');
k2 = parallel.gpu.CUDAKernel('test.ptx', 'test.cu', 'doubleFoo');
out1 = feval(k1, -4)
out2 = feval(k1, -5)
out3 = feval(k2, -4)
out4 = feval(k2, -5)
实验结果:
out1 = 16
out2 = 24 //This hasn't to be 25 !!??
out3 = 16
out4 = 25.000
编辑:
在调查了@Robert Crovella的建议后,我发现Matlab中的命令窗口显示out4=25.000
,而变量窗口显示out4 = 24.9999981
的内容。
每个人都应该非常小心,因为powf
函数(24.9999981
而不是25
)的输出可能会出现一个小错误,该错误可能会传播并成为大型计算的问题
2条答案
按热度按时间0pizxfdo1#
我认为这是由于
feval
数据类型的不明智使用。在我看来,
feval
将返回类型转换为与参数类型相同的类型。这是有意义的,因为返回类型是从指向该参数的传递参数的指针中提取的。请注意,
powf
采用float
参数并返回float
,pow
采用double
参数并返回double
。int
量在CUDA数学API中没有单独的函数(原型),因此如果您使用它们,它们将被转换为浮点类型或从浮点类型转换而来。以下是我在纯CUDA C++中看到的内容:
请注意:
1.对于
(-5,2)
,CUDApowf
返回24.9999981.如果我们将其转换为
int
,它将被截断为241.如果我们将其转换为
double
,然后四舍五入到3位小数,正确的四舍五入结果将是25.000,正如matlab输出中显示的那样建议:
1.别这样
1.不要将整数类型与浮点函数一起使用(尤其是转换结果)
1.如果你想平方某个东西,只要把它和它本身相乘就可以了。这肯定会比使用
powf(x, 2)
更快,而且可能也会更准确。如果你想知道“为什么CUDA
powf(-5, 2)
返回24.999998?",请在另一个问题中问这个问题。编程手册中定义了准确度,我有理由相信这福尔斯公布的误差范围内。Here是pow
“怪异”的另一个例子。7ajki6be2#
作为Robert Crovella的answer的附录:CUDA是C++的一个子集,因此提供了重载数学函数。特别是它提供了
pow()
的以下四个变体:如果你用
cuobjdump --dump-sass
检查这些变量的机器代码,你会发现使用了四种不同的实现。正如Robert Crovella指出的,对于平方的特殊情况,最好只使用乘法,但是如果你愿意,你当然可以使用pow()
,如下面的代码所示(为了简洁起见,省略了错误检查):以上程序的输出应如下所示: