C 中的多个动态浮点格式说明符

Multiple dynamic float format specifier in C

我遇到过几次使用 float 变量的情况,这些变量必须 printf 正确格式化并具有一定数量的小数位。我知道在 C 中,您可以通过使用以下符号来指定小数位数和总位数,从而轻松地格式化 float 变量:

// Where x and y are natural numbers
printf("%<x>.<y>f", myFloat);

而且我还知道您可以使用两个变量将 * 符号用于 dynamically format a float,如下所示:

// Where x and y are natural numbers
printf("%*.*f", x, y, myFloat);

但是我最近陷入了一个非常多余的情况:当我不得不printf输出多个float数字时相同的动态格式 我必须重复这两个 xy 变量数千次。 我总是喜欢 #define 我的常数,这样我就可以很容易地改变它们未来,所以我的程序看起来像:

#define FLOAT_FORMAT_SIZE 10
#define FLOAT_FORMAT_PRECISION 3

int main(int argc, char *argv[]) {
    // Anything I have to do...

    // Output
    printf("%*.*f, %*.*f, %*.*f, %*.*f",
        FLOAT_FORMAT_SIZE, FLOAT_FORMAT_PRECISION, myFloat1,
        FLOAT_FORMAT_SIZE, FLOAT_FORMAT_PRECISION, myFloat2,
        FLOAT_FORMAT_SIZE, FLOAT_FORMAT_PRECISION, myFloat3,
        FLOAT_FORMAT_SIZE, FLOAT_FORMAT_PRECISION, myFloat4
    );

    return 0;
}

现在你可以看到这有点太多余了,我想知道:有没有什么特殊的系统常量或格式技术我可以用于避免为每个 float I printf out?

一遍又一遍地重复相同的值

我不想创建自己的函数来打印这些数字,我显然可以做到,但如果可能的话,我想在较低的层次上解决它。

您可以在 运行 时间创建格式字符串,并根据需要多次使用它。

// Create the format string.
char formatString[100]; // Make it large enough.
sprintf(formatString, "%%%d.%df", FLOAT_FORMAT_SIZE, FLOAT_FORMAT_PRECISION);

// Use the format string to print a float
printf(formatString, myFloat1);
printf(formatString, myFloat2);
printf(formatString, myFloat3);
printf(formatString, myFloat4);

您可以编写一个宏来定义打印浮点数的默认方式:

#define FLOAT_FORMAT_SIZE 10
#define FLOAT_FORMAT_PRECISION 3

#define M_STR(x) M_STR_(x)
#define M_STR_(x) #x

#define FLOAT_FMT                       \
    "%" M_STR(FLOAT_FORMAT_SIZE)        \
    "." M_STR(FLOAT_FORMAT_PRECISION)   \
    "f"

那么您的 printf 语句将如下所示:

printf(FLOAT_FMT ", " FLOAT_FMT ", " FLOAT_FMT ", " FLOAT_FMT,
    myFloat1, myFloat2, myFloat3, myFloat4);

宏及其使用方式均使用字符串文字连接。格式字符串是在编译时创建的,因此会触发有关格式和参数类型不匹配的警告。

当然,格式字符串是否非常可读是有争议的。

编辑:这是如何工作的?宏 M_STR "stringifies" 它的参数;它把它变成一个 C 字符串文字。宏定义中可用的运算符 # 实现了这一点。

必须使用到辅助宏的间接寻址M_STR_,这样宏参数将首先展开。如果没有这个辅助宏,M_STR(FLOAT_FORMAT_SIZE) 将扩展为 "FLOAT_FORMAT_SIZE"。通过 M_STR_ 绕道,宏 FLOAT_FORMAT_SIZE 在变成字符串之前被扩展,结果字符串是 "10".

因此,宏 FLOAT_FMT 扩展为

"%"   "10"   "."   "3"   "f"

C 编译器连接相邻的字符串文字,因此宏的最终结果等同于 "%10.3f"

构造格式字符串时也是如此:

printf(FLOAT_FMT ", " FLOAT_FMT ", " FLOAT_FMT ", " FLOAT_FMT,
    myFloat1, myFloat2, myFloat3, myFloat4);

有效:

printf("%10.3f, %10.3f, %10.3f, %10.3f",
    myFloat1, myFloat2, myFloat3, myFloat4);

此解决方案的缺点是字符串和宏的并置有点难以阅读。