我最近发现自己有效地做到了这一点:
double s[4] = {10., 2., 3.1, 4.1};
dscal_(4, 3., s, -1);
assert( s[1] == 2.*3. );
dscal_
是最简单的 BLAS 函数,将一个数组乘以一个标量,它的签名是:
void sscal(int, double, double*, int); // doesn't seem to return anything useful
在我特定的 BLAS 发行版(Fedora 28 附带)中,这给出了一个静默错误,因为该函数似乎没有做任何事情。此外,dscal_
似乎甚至没有返回错误代码,因此如果没有包装函数,我无法捕捉到这个错误(我的数组在运行时有正向或负向跨步,但我无法在所有情况下控制值)。
所有这些案例都失败了:
double s[4] = {10., 2., 3.1, 4.1};
dscal_(4, 3., s, -1); // negative incx does nothing
dscal_(4, 3., &s[4], -1); // negative incx does nothing
dscal_(-4, 3., &s[4], 1); // negative n does nothing
dscal_(-4, 3., &s[4], -1); // negative n or incx does nothing
assert( s[1] == 2. );
这告诉我,尽管可能没有在任何地方记录步幅 ( incx
) 必须为正数,(以及大小)。幸运的是,对于许多 BLAS 函数,调用可以转换为正步幅。我需要一个包装函数来调试这个,所以写了下面的包装函数:
void signed_dscal(int n, double alpha, double* x, int incx){
int prod = incx*n;
if(prod > 0) dscal(abs(n), alpha, x, abs(incx));
else dscal(abs(n), alpha, x + prod, abs(incx));
}
通过这种方式,我可以signed_dscal
用正面或负面的步幅和大小来跟注。
int main(){
{
double d[4] = {10., 2., 3.1, 4.1};
signed_dscal(4, 3., d, 1);
assert( d[1] == 6. );
}
{
double d[4] = {10., 2., 3.1, 4.1};
signed_dscal(4, 3., &d[4], -1);
assert( d[1] == 6. );
}
{
double d[4] = {10., 2., 3.1, 4.1};
signed_dscal(-4, 3., &d[4], 1);
assert( d[1] == 6. );
}
{
double d[4] = {10., 2., 3.1, 4.1};
signed_dscal(-4, 3., d, -1);
assert( d[1] == 6. );
}
return 0;
}
(请注意,这incx=0
仍然是无法修改的情况。)
我仍然不明白这背后的逻辑是什么。也许 BLAS 的某些实现会默认让您执行此操作,而其他实现会尝试防止无限循环,其副作用将假定为正步幅值。