Подтвердить что ты не робот

Проверка того, что сдвиг вправо/сдвиг C/С++ является арифметическим для конкретного компилятора?

Согласно стандарту C/С++ (см. эту ссылку), оператор → в C и С++ не обязательно является арифметическим сдвигом для подписанные номера. Реализация компилятора зависит от того, сдвинуты ли 0 (логические) или знаковый бит (арифметические), так как биты смещены вправо.

Будет ли этот код работать с ASSERT (fail) во время компиляции для компиляторов, реализующих логический сдвиг вправо для целых чисел со знаком?

#define COMPILE_TIME_ASSERT(EXP) \
    typedef int CompileTimeAssertType##__LINE__[(EXP) ? 1 : -1]

#define RIGHT_SHIFT_IS_ARITHMETIC \
    ( (((signed int)-1)>>1) == ((signed int)-1) )

// SHR must be arithmetic to use this code
COMPILE_TIME_ASSERT( RIGHT_SHIFT_IS_ARITHMETIC );
4b9b3361

Ответ 1

Выглядит хорошо! Вы также можете настроить компилятор для выпуска файла сборки (или загрузить скомпилированную программу в отладчике) и посмотреть, какой код операции он испускает для signed int i; i >> 1;, но не автоматический, как ваше решение.

Если вы когда-либо находите компилятор, который не реализует арифметический сдвиг вправо значного числа, я хотел бы услышать об этом.

Ответ 2

Зачем утверждать? Если ваш оператор смены компилятора не соответствует вашим потребностям, вы можете изящно исправить ситуацию, указав результат. Кроме того, иногда время выполнения достаточно хорошее. В конце концов, оптимизатор компилятора может сделать время компиляции вне времени выполнения:

template <typename Number>
inline Number shift_logical_right(Number value, size_t bits)
{
    static const bool shift_is_arithmetic = (Number(-1) >> 1) == Number(-1);
    const bool negative = value < 0;
    value >>= bits;
    if (!shift_is_arithmetic && negative) // sign extend
        value |= -(Number(1) << (sizeof(Number) * 8 - bits));
}

static const bool можно оценить во время компиляции, поэтому, если shift_is_arithmetic гарантированно будет true, каждый компилятор, заслуживающий его соли, исключит все предложение if и конструкцию const bool negative как мертвый код.

Примечание: код адаптирован из функции Mono encode_sleb128: здесь.

Обновление

Если вы действительно хотите прервать компиляцию на машинах без арифметического сдвига, вам все же лучше не полагаться на препроцессор. Вы можете использовать static_assert (или BOOST_STATIC_ASSERT):

static_assert((Number(-1) >> 1) == Number(-1), "Arithmetic shift unsupported.");

Ответ 3

Из ваших различных комментариев вы говорите об использовании этой кроссплатформенности. Убедитесь, что ваши компиляторы гарантируют, что при компиляции для платформы их операторы времени компиляции будут вести себя так же, как и операторы времени выполнения.

Пример отличающегося поведения можно найти с числами с плавающей запятой. Ваш компилятор выполняет математику с постоянным выражением с одинарной, двойной или расширенной точностью, если вы приводите обратно к int? Такие как

constexpr int a = 41;
constexpr int b = (a / 7.5);

Я хочу сказать, что вы должны убедиться, что ваши компиляторы гарантируют то же поведение во время выполнения, что и во время компиляции, когда вы работаете с таким количеством различных архитектур.

Вполне возможно, что компилятор может внутренне подписывать, но не генерировать намеченный код операции на цели. Единственный способ убедиться в этом - это проверить во время выполнения или посмотреть на результаты сборки.

Это не конец света, чтобы посмотреть на результаты сборки... Сколько существует разных платформ? Поскольку это так критично для производительности, просто сделайте "работу" по просмотру 1-3 строк вывода ассемблера для 5 различных архитектур. Это не так, как если бы вам приходилось погружаться через весь вывод сборки (обычно!), Чтобы найти свою строку. Это очень легко сделать.