科学记数法对C中的整数常数安全吗

Is scientific notation safe for integer constants in C?

本文关键字:整数 常数 安全      更新时间:2023-10-16

一段时间以来,我一直在使用科学记数法表示常量中的10的大幂,这样我就不必计算零了。例如

#define DELAY_USEC 1e6

一位同事指出,这是不安全的,因为它不是整数,也不能保证总是等于1000000恰好。文档似乎证实了这一点,但我想知道它在实用性上是否属实。有没有什么方法可以使用简写安全地声明十次方整数?在define中将其强制转换为int是否安全?

理论上没有。这两种语言都没有指定如何表示浮点值,也没有指定可以精确表示哪些值。(更新:显然,C11确实推荐了一种表示。C++和旧的C方言不推荐(。

在实践中,是的,对于相当大的值范围。您可能遇到的任何实现都将使用64位IEEE表示double。这可以精确地表示高达253(大约9x1015(的任何整数值。它当然可以表示由32位整数类型表示的任何东西。

您想要使用用户定义的文字:

constexpr long long operator "" _k(long long l) {
    return l * 1000;
}
constexpr long long operator "" _m(long long l) {
    return l * 1000 * 1000;
}

然后你可以简单地做:

long long delay = 1_m;
long long wait = 45_k;

您特别询问10的幂。1e6正好是一百万。你可以在没有任何不好的事情发生的情况下进入1e22。但是,请注意,在C++和C中,1e6都是double常数,而不是整数常数。

十的负幂是另一回事。1e-1是不精确的,所有较低的幂也是不精确的。

似乎gcc假设一个使用科学符号定义的常数为浮点数,除非它是强制转换的。

一个简单的C代码显示如下:

#include <stdio.h>
#define DELAY_USEC_FP  1e6
#define DELAY_USEC_INT (unsigned int) 1e6
int main()
{
    printf("DELAY_USEC_FP: %fn", DELAY_USEC_FP);
    printf("DELAY_USEC_INT: %un",  DELAY_USEC_INT);
    return 0;
}

在x86-64机器上,gcc生成以下汇编代码($ gcc -S define.c(:

[...]
; 0x4696837146684686336 = 1e6 in double-precision FP IEEE-754 format
movabsq $4696837146684686336, %rax
[...]
call    printf
movl    $1000000, %esi
[...]
call    printf
movl    $0, %eax

如上所述,10e15和10e22是分别以简单浮点格式和双精度浮点格式精确表示的十个数字的最大幂。

使用32位或64位整数类型不能表示十次幂以上的数字。

由于double的规范留出了52位供您使用,因此在小于INT_MAX的东西上永远不会出现舍入错误。你的"分数分量"只是你的整数,而你的"指数"将是1,浮点运算并不困难。

这真的不安全,因为编译器会认为它是一个浮点数,所以精度限制在53位,而不是64位整数(长整型(

http://en.wikipedia.org/wiki/Floating_point