Является ли научное обозначение безопасным для целочисленных констант в C?
Некоторое время я представлял большие константы в константах с использованием научной нотации, так что мне не нужно было подсчитывать нули. например.
#define DELAY_USEC 1e6
Коллега отметил, что это небезопасно, потому что это не целое число и не гарантируется, что он всегда будет равным 1000000. Документация, похоже, подтверждает это, но мне интересно, насколько это верно в практичности. Есть ли способ безопасно объявить целое число из десяти слов с использованием сокращенного названия? Можно ли просто передать его в int в определении?
Ответы
Ответ 1
В теории нет. Ни один из языков не указывает, как представляются значения с плавающей запятой, или какие значения могут быть представлены точно. (ОБНОВЛЕНИЕ: по-видимому, C11 действительно рекомендует представление. С++ и более старые диалекты C, не).
На практике, да, для довольно большого диапазона значений. Любая реализация, с которой вы с вероятностью столкнетесь, будет использовать 64-битное представление IEEE для double
. Это может представлять собой любое целое значение до 2 53 (приблизительно 9x10 15). Он может, безусловно, представлять что-либо, представляемое 32-битным целым типом.
Ответ 2
Вы хотите использовать определенные пользователем литералы:
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;
Ответ 3
Вы спрашиваете конкретно о силе десяти. 1e6
будет ровно один миллион. Вы можете подняться до 1e22
без каких-либо неприятностей. Однако обратите внимание, что как в С++, так и в C, 1e6
является константой double
, а не целочисленной константой.
Отрицательные силы десяти - это совсем другая история. 1e-1
неточно, как и все нижние уровни.
Ответ 4
Вы никогда не получите ошибок округления на чем-то меньше INT_MAX
, так как спецификация для double
выделяет 52 бит для использования. Ваш "дробный компонент" - это просто ваше целое число, и ваш "показатель" будет равен 1, а с плавающей запятой не будет с этим бороться.
Ответ 5
Это действительно небезопасно, потому что компилятор будет рассматривать его как число с плавающей запятой, поэтому точность ограничена 53 битами вместо 64 битов целых чисел (long int), которые вы можете прочитать о представлении чисел с плавающей запятой
http://en.wikipedia.org/wiki/Floating_point
Ответ 6
Похоже, что 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
Как указано здесь, 10e15 и 10e22 - это максимальная мощность десяти чисел, которые имеют точное представление в простых и с двойной точностью, точечный формат, соответственно.
Увеличенная мощность десяти чисел не может быть представлена с использованием 32-битных или 64-разрядных целых типов.