正确设计C代码,处理单精度和双精度浮点?

Dav*_*d H 8 c floating-point

我正在用C开发一个专用数学函数库.我需要为库提供一个处理单精度和双精度的功能.这里重要的一点是"单个"函数应该在内部使用"单个"算术(对于"双"函数).

作为示例,请看一下LAPACK(Fortran),它提供了每个函数的两个版本(SINGLE和DOUBLE).还有C数学库(例如,expfexp).

为了澄清,我想支持类似于以下(人为)示例的内容:

float MyFloatFunc(float x) {
    return expf(-2.0f * x)*logf(2.75f*x);
}

double MyDoubleFunc(double x) {
    return exp(-2.0 * x)*log(2.75*x);
}
Run Code Online (Sandbox Code Playgroud)

我考虑过以下方法:

  1. 使用宏作为函数名称.这仍然需要两个独立的源代码库:

    #ifdef USE_FLOAT
    #define MYFUNC MyFloatFunc
    #else
    #define MYFUNC MyDoubleFunc
    #endif
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将宏用于浮点类型.这允许我在两个不同版本之间共享代码库:

    #ifdef USE_FLOAT
    #define NUMBER float
    #else
    #define NUMBER double
    #endif
    
    Run Code Online (Sandbox Code Playgroud)
  3. 刚开发两个独立的库,忘记尝试保存头痛.

有人有推荐或其他建议吗?

Pas*_*uoq 7

对于多项式近似,插值和其他固有近似数学函数,您不能在双精度和单精度实现之间共享代码,既不会在单精度版本中浪费时间,也不会在双精度版本中浪费时间. .

然而,如果你走单个代码库的路线,下面的代码应该适用于常量和标准库函数:

#ifdef USE_FLOAT
#define C(x) x##f
#else
#define C(x) x
#endif

... C(2.0) ... C(sin) ...
Run Code Online (Sandbox Code Playgroud)


Dmi*_*tri 5

(部分灵感来自Pascal Cuoq的答案)如果你想要一个具有float和双重版本的库的库,你可以将递归#includes与宏结合使用.它不会产生最清晰的代码,但它确实允许您对两个版本使用相同的代码,并且混淆足够薄,它可能是可管理的:

mylib.h:

#ifndef MYLIB_H_GUARD
  #ifdef MYLIB_H_PASS2
    #define MYLIB_H_GUARD 1
    #undef C
    #undef FLT
    #define C(X) X
    #define FLT double
  #else
    /* any #include's needed in the header go here */

    #undef C
    #undef FLT
    #define C(X) X##f
    #define FLT float
  #endif

  /* All the dual-version stuff goes here */
  FLT C(MyFunc)(FLT x);

  #ifndef MYLIB_H_PASS2
    /* prepare 2nd pass (for 'double' version) */
    #define MYLIB_H_PASS2 1
    #include "mylib.h"
  #endif
#endif /* guard */
Run Code Online (Sandbox Code Playgroud)

mylib.c:

#ifdef MYLIB_C_PASS2
  #undef C
  #undef FLT
  #define C(X) X
  #define FLT double
#else
  #include "mylib.h"
  /* other #include's */

  #undef C
  #undef FLT
  #define C(X) X##f
  #define FLT float
#endif

/* All the dual-version stuff goes here */
FLT C(MyFunc)(FLT x)
{
  return C(exp)(C(-2.0) * x) * C(log)(C(2.75) * x);
}

#ifndef MYLIB_C_PASS2
  /* prepare 2nd pass (for 'double' version) */
  #define MYLIB_C_PASS2 1
  #include "mylib.c"
#endif
Run Code Online (Sandbox Code Playgroud)

每个文件#include本身在第二遍中使用不同的宏定义一次,以生成使用宏的代码的两个版本.

但是,有些人可能会反对这种方法.