使用stdint.h和ANSI printf?

3
我正在编写一个大数库,并希望使用高效的数据类型来表示数字。特别是对于数字,我将使用整数类型,对于加法和乘法的中间结果,我将使用长整型(如果严格双倍于整数类型)。
我会使用一些C99功能,但尽可能符合ANSI C标准。
目前我的大数库中包含以下内容:
#include <stdint.h>

#if defined(__LP64__) || defined(__amd64) || defined(__x86_64) || defined(__amd64__) || defined(__amd64__) || defined(_LP64)
typedef uint64_t u_w;
typedef uint32_t u_hw;
#define BIGNUM_DIGITS 2048
#define U_HW_BITS 16
#define U_W_BITS 32
#define U_HW_MAX UINT32_MAX
#define U_HW_MIN UINT32_MIN
#define U_W_MAX UINT64_MAX
#define U_W_MIN UINT64_MIN
#else
typedef uint32_t u_w;
typedef uint16_t u_hw;
#define BIGNUM_DIGITS 4096
#define U_HW_BITS 16
#define U_W_BITS 32
#define U_HW_MAX UINT16_MAX
#define U_HW_MIN UINT16_MIN
#define U_W_MAX UINT32_MAX
#define U_W_MIN UINT32_MIN
#endif

typedef struct bn
{
        int sign;
        int n_digits; // #digits should exclude carry (digits = limbs)
        int carry;
        u_hw tab[BIGNUM_DIGITS];
} bn;

由于我还没有编写将大数以十进制形式写入的过程,因此我必须分析中间数组并使用printf打印每个数字的值。然而,我不知道在printf中使用哪种转换说明符。最好是将16进制编码的数字写入终端。
根本问题在于,我想要两种数据类型,一种比另一种长两倍,并使用标准的转换说明符与printf一起使用。如果int为32位且long为64位,则最理想。但是我不知道如何使用预处理器来保证这一点,当使用仅依赖于标准类型的函数(如printf)时,我不再知道该使用什么。
2个回答

4
您可以使用来自<inttypes.h>的宏来帮助解决问题:
#if defined(__LP64__) || defined(__amd64) || defined(__x86_64) || defined(__amd64__) || defined(__amd64__) || defined(_LP64)
typedef uint64_t u_w;
typedef uint32_t u_hw;
#define BIGNUM_DIGITS 2048
#define U_HW_BITS 16
#define U_W_BITS 32
#define U_HW_MAX UINT32_MAX
#define U_HW_MIN UINT32_MIN
#define U_W_MAX UINT64_MAX
#define U_W_MIN UINT64_MIN
#define PRI_U_HW  PRIu32    // use for formatting a `u_hw` type
#define PRI_U_W   PRIu64    // use for formatting a `u_w` type
#else
typedef uint32_t u_w;
typedef uint16_t u_hw;
#define BIGNUM_DIGITS 4096
#define U_HW_BITS 16
#define U_W_BITS 32
#define U_HW_MAX UINT16_MAX
#define U_HW_MIN UINT16_MIN
#define U_W_MAX UINT32_MAX
#define U_W_MIN UINT32_MIN
#define PRI_U_HW  PRIu16    // use for formatting a `u_hw` type
#define PRI_U_W   PRIu32    // use for formatting a `u_w` type
#endif

然后:

printf( "some u_w variable: %" PRI_U_W "\n", u_w_var);    
printf( "some u_hw variable: %" PRI_U_HW "\n", u_hw_var);

它们并不美观,但这就是 C99 的实现方式。


你的意思是将定义括在双引号中吗?无论如何,这是一个很方便的提示。我以为printf手册中会记录下这个问题,但我还在想C99将如何处理这些类型。谢谢。 - snap
1
在示例的第一部分中,PRI_U_HWPRI_U_W只是C99 PRIuXX值的别名,它们将成为字符串字面量。当您使用它们(如第二个代码片段中的2个printf()示例)时,必须在引号外使用它们(它们提供自己的引号),并依赖于C执行的相邻字符串字面量的连接,在翻译的“第6阶段”执行。就像我说的,这有点丑陋。 - Michael Burr
我明白了。顺便问一下,你觉得有没有办法避免使用处理器的低效操作,而只使用int/long?我只需要两种类型,其中一个至少是另一个大小的两倍。我猜char和short可以解决问题,但如果可能的话,我更愿意使用更大的数据类型,如int/long/long long。 - snap
@nn:我可能会考虑只使用uint32_t和uint64_t。我认为几乎所有32位编译器都支持64位int类型(即使它不是“long long”,例如VC6有__int64类型)。但是,您必须能够说明您正在限制对具有64位int类型的编译器的支持,只有您可以说出这是否可接受。 - Michael Burr

3

ANSI C没有提供有关intlong大小的保证,而我认为long long不是ANSI类型。如果您不愿意或不能使用C99,则唯一安全、可移植的解决方案是编写一个配置脚本,该脚本将创建使用sizeof查找具有所需属性的整数类型对的C程序。然后在该脚本中生成包括printf格式宏的宏。

还有可能的原因是您没有使用C99是因为您正在将代码移植到某个奇怪的平台上,该平台没有C99编译器。在这种情况下,您可以找出可行的方法,将其放入头文件中,并不必担心可移植性。

C99并不美观,但它确实解决了一些令人烦恼的C问题。


感谢您揭开了这个过程的神秘面纱。我猜这就是大多数autoconf程序所做的。您建议手动完成,还是让一些工具来完成艰苦的工作?我只知道autoconf,但似乎对于启动小项目来说过于复杂了。 - snap
@nn:我对GNU Autotools感到非常反感。我建议手写一个POSIX sh脚本。 - Norman Ramsey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接