3

我正在使用 C 进行一些科学计算,并且需要移动很多向量。

我已经定义了表单的一些基本乘法和加法函数

add(int size, double *out, double *x, double *y)

但是对于复杂的操作,代码很快就会变得冗长且难以阅读。

是否可以定义内联运算符 (V1 + V2) ?或者任何一般的最佳实践可以更容易地检查数学错误的代码?也许有一些#define 恶作剧?

4

4 回答 4

4

起初我认为这是一个 C++ 问题,出于某种原因!

如果您可以使用 C++,您也许可以使用 STL 数组(其中大小是模板参数,而不是存储值)。

它可能看起来像这样:

std::array<double, 7> a = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0};
std::array<double, 7> b = {0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0};
auto c = a + b;
auto d = a + b + c;

您可以为 operator+ 定义运算符重载。例如:

#include <array>
// ...
// (version 1) returns a new copy/temporary
template <class T, size_t size>
std::array<T, size> operator+(const std::array<T, size>& a, 
                              const std::array<T, size>& b)
{
    std::array<T, size> c;
    // add them however you want; simple version:
    for (size_t i = 0; i < size; ++i)
    {
        c[i] = a[i] + b[i];
    }
    return c;
}

// (version 2) no temporaries via rvalue ref/move semantic
template <class T, size_t size>
std::array<T, size>&& operator+(std::array<T, size>&& a, 
                                const std::array<T, size>& b)
{
    for (size_t i = 0; i < size; ++i)
    {
        a[i] += b[i];
    }
    return std::move(a);
}

以便:

auto c = a + b;       // invokes version 1
auto d = a + b + c;   // invokes version 1 for (b+c) 
                      // and then version 2 for a+(temp)

所以你在任何操作链中最多使用一个临时的。

std::array 的内存布局应该与本机数组相同,因此如果需要,您应该能够很容易地将这段代码“注入”到现有程序中(通过(ab)使用强制转换、typedef 或预处理器)接触最少量的现有代码。

于 2012-05-04T02:02:11.707 回答
3

如果您不介意该问题的非标准解决方案,英特尔编译器支持 C 中数组表示法的扩展。这是一个链接:

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/optaps/common/optaps_par_cean_prog.htm

基本上,您可以编写如下代码:

int A[4] = { 1, 2, 3, 4 };
int B[4] = { 4, 3, 2, 1 };
int C[4];
C[:] = A[:] + B[:];

它还提供许多数学函数的矢量版本,因此您可以执行以下操作:

B[:] = cosf(C[:]);

作为额外的奖励,如果您这样编写代码,它会在 SSE/AVX 指令中被矢量化。

于 2012-05-04T06:28:36.803 回答
2

这被称为“运算符重载”,它不是C 的一个特性。但它C++ 的一个特性。如果您可以选择 C++,那么有很多关于如何重载运算符的教程。有些人认为它是邪恶的,因为:

  1. 当代码脱离上下文时,很难判断实际会发生什么。例如,你可以在那里看到操作符,但你怎么知道它们是内置的还是重载的呢?

  2. 可以重载运算符并对它们应用不常见的含义,例如(这是我在实际代码中看到的),您可以重载“/”运算符(除法运算符)作为路径分隔符一个以独立于操作系统的方式处理文件名的类:

    Filename f = Filename::MainFileSystem() / "folder1" / "folder2" / "file.txt";
    

    不要这样做。

使用重载运算符还有一些好处:

  1. 代码可以大大简化。而不是多个函数调用,您可以使用

    result = vec1 + vec2 + vec3;
    

    请注意,如上所述,仅通过查看该行代码很难判断是否会调用任何函数,但这种困难是否超过简化的好处是主观的。

  2. 您可以执行一些漂亮的技巧,例如智能指针,在不修改运算符的原始语义的情况下为您提供附加功能。

于 2012-05-04T02:24:21.277 回答
1

是否可以定义内联运算符 (V1 + V2) ?

不,但它是用 C++ 编写的。

于 2012-05-04T00:41:19.680 回答