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

eqoofvh9  于 2022-12-11  发布在  其他
关注(0)|答案(6)|浏览(196)

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

#define DELAY_USEC 1e6

一位同事指出,这是不安全的,因为它不是整数,而且不能保证总是等于1000000 exactly。文档似乎证实了这一点,但我想知道它在实践中是否正确。有没有什么方法可以安全地使用速记声明10的幂整数?在define中将其转换为int是否安全?

xn1cxnb4

xn1cxnb41#

从理论上讲,没有。这两种语言都没有指定如何表示浮点值,也没有指定哪些值可以精确表示。(更新:显然,C11确实推荐了一种表示法。C++和更老的C方言则不推荐。
实际上,是的,对于相当大范围的值。任何你可能遇到的实现都将使用64-bit IEEE representation来表示double。这可以精确地表示任何整数值,直到253(大约9x1015)。它当然可以表示任何可以用32位整数类型表示的值。

k0pti3hp

k0pti3hp2#

要使用user-defined literals

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;
vmpqdwk3

vmpqdwk33#

如果你问的是10的幂,1e6正好是100万。你可以一直查到1e22而不会发生任何问题。但是,请注意,在C++和C中,1e6都是double常量,而不是整型常量。
10的负幂则是另一回事。1e-1是不精确的,所有较低的幂也是如此。

uurity8g

uurity8g4#

看起来gcc假定一个使用科学记数法定义的常量为浮点数,除非它是强制转换的。
一个简单的C代码显示了这一点:

#include <stdio.h>

#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

here所述,10 e15和10 e22是10的最大幂数,它们分别以简单和双精度浮点格式精确表示。
无法使用32位或64位整数类型表示更大的10的幂。

mwkjh3gx

mwkjh3gx5#

对于小于INT_MAX的值,永远不会出现舍入误差,因为double的规范将52 bits for you to use排除在外。“小数部分”只是整数,“指数”将是1,浮点不受此影响。

lhcgjxsq

lhcgjxsq6#

这真的不安全,因为编译器会将其视为浮点数,因此精度限制为53位,而不是64位整数(长整型)。
http://en.wikipedia.org/wiki/Floating_point

相关问题