Является ли научное обозначение безопасным для целочисленных констант в 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-разрядных целых типов.