科学记数法对于C中的整数常量是否安全?
有一段时间,我一直用科学记数法表示常量中10的大功率,所以我不必计算零。 例如
#define DELAY_USEC 1e6
一位同事指出这不安全,因为它不是一个整数,并且不能保证总是完全等于1000000。 文档似乎证实了这一点,但我想知道它在实用性方面是否真实。 有没有办法用速记安全地声明十进制的整数? 将它强制转换为定义中的int是否安全?
从理论上讲,没有。 两种语言都没有指定如何表示浮点值,或者哪些值可以精确表示。 (更新:显然,C11确实推荐了一种表示法.C ++和旧的C方言,不这样做)。
在实践中,是的,对于相当大的价值范围。 您可能遇到的任何实现都将使用64位IEEE表示forms进行double
。 这可以精确地表示高达2 53 (大约9×10 15 )的任何整数值。 它当然可以表示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;
你具体问十个权力。 1e6
将是一百万。 你可以在没有发生任何不良事件的情况下升至1e22
。 但是,请注意,在C ++和C中, 1e6
是double
常量,而不是整数常量。
十的负面力量是另一回事。 1e-1
和所有低权力都是不精确的。
似乎gcc
假定使用科学记数法定义的常量作为浮点数,除非它是强制转换的。
一个简单的C代码显示了这个:
#include #define DELAY_USEC_FP 1e6 #define DELAY_USEC_INT (unsigned int) 1e6 int main() { printf("DELAY_USEC_FP: %f\n", DELAY_USEC_FP); printf("DELAY_USEC_INT: %u\n", 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分别是10个数字的最大功率,它们分别具有简单和双精度浮点格式的精确表示。
使用32位或64位整数类型无法表示十个数字的更大功率。
你永远不会在小于INT_MAX
东西上得到舍入错误,因为double
的规范设置了52位供你使用 。 你的“小数分量”只是你的整数,你的“指数”将是1,浮点数并不挣扎。
它真的不安全,因为编译器会将它视为浮点数,因此精度限制为53位而不是64位整数(long int),您可以阅读有关浮点数numbrers的更多信息。