Есть ли компиляторы C99, где с настройками по умолчанию -1 & gt; & gt; 1! = -1?

Многие люди часто указывают на дискуссии об операторе правого сдвига, что в стандарте C прямо говорится, что эффект смещения вправо отрицательного числа определяется реализацией. Я могу понять историческую основу этого утверждения, учитывая, что компиляторы C использовались для генерации кода для множества платформ, которые не используют арифметику с двумя дополнениями. Однако вся разработка нового продукта, о которой я знаю, была сосредоточена вокруг процессоров, которые не имеют внутренней поддержки какой-либо целочисленной арифметики, кроме двух-дополнения.

Если код хочет выполнить поэтапное целочисленное деление со знаком со степенью двойки, и он будет выполняться только для текущей или будущей архитектуры, есть ли реалистический опасность того, что любой будущий компилятор будет интерпретировать оператор сдвига вправо как что-то еще? Если есть реальная возможность, есть ли какой-нибудь хороший способ обеспечить это без ущерба для читаемости, производительности или того и другого? Существуют ли какие-либо другие зависимости, которые бы оправдывали прямое предположение о поведении оператора (например, код будет бесполезен в реализациях, которые не поддерживают функцию X, и реализации вряд ли будут поддерживать X, если они не используют сдвиги вправо с расширенными знаками) )?

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

7

Решение

Это только одна из многих причин, почему это так, но рассмотрим случай обработки сигнала:

1111 0001 >> 1
0000 1111 >> 1

В форме сдвига правой арифметики (SRA), на которую вы ссылаетесь, вы получите следующее:

1111 0001 >> 1 = 1111 1000
OR
-15 >> 1 = -8

0000 1111 >> 1 = 0000 0111
OR
15 >> 1 = 7

Так в чем проблема? Рассмотрим цифровой сигнал с амплитудой 15 «единиц». Разделив этот сигнал на 2 должен привести к эквивалентному поведению независимо от знака. тем не мение, с SRA, как указано выше, положительный сигнал 15 приведет к сигналу с 7 амплитудой, а отрицательный сигнал 15 приведет к сигналу с 8 амплитудой. Эта неравномерность приводит к смещению постоянного тока на выходе. По этой причине некоторые процессоры DSP предпочитают реализовывать арифметику сдвига вправо или от других в целом. Поскольку стандарт C99 сформулирован как есть, эти процессоры могут быть совместимы.

На этих процессорах -1 >> 1 == 0

Связанные Вики

3

Другие решения

Теоретически, в настоящее время существуют тонкости в реализациях компилятора, которые могут злоупотреблять так называемым «неопределенным поведением», помимо того, что бэкэнд-процессор будет делать с действительными целыми числами в регистрах (или «файлах», или местах памяти, или чего-либо еще):

  1. Кросс-компиляторы — обычные вещи: компилятор может злоупотреблять спецификациями, зависящими от реализации, при выполнении простых вычислений сам. Рассмотрим случай, когда целевая архитектура реализует это одним способом, а хостинг — другим. В вашем конкретном примере константы времени компиляции могут в конечном итоге равняться 1, даже если в противном случае выходные данные сборки в целевой архитектуре в противном случае дадут 0 (я не могу думать о такой архитектуре). А потом опять наоборот. Не будет никакого требования (кроме жалоб пользователей) для разработчика компилятора, чтобы не заботиться об этом.

  2. Рассмотрим CLANG и другие компиляторы, которые генерируют промежуточный абстрактный код. Ничто не мешает механике типов оптимизировать некоторые операции вплоть до последнего бита в промежуточное время на некоторых путях кода (т. Е. Когда это может привести к сокращению кода до констант, приходит на ум сворачивание цикла), оставляя при этом бэкэнд сборки для решения этой проблемы во время выполнения в другие пути. Другими словами, вы могли видеть смешанный поведение. В этом виде абстракции от исполнителя не требуется подчиняться каким-либо стандартам, отличным от того, чего ожидает язык C. Представьте себе случай, когда вся целочисленная математика выполняется библиотеками арифметики произвольной точности, а не прямым отображением в целые числа процессоров. Реализация может принять решение по любой причине, что это неопределенное значение и будет возвращать 0. Она может сделать это для любого арифметического неопределенного поведения со знаком, и в стандарте ISO C есть много такого, особенно упаковка и тому подобное.

  3. Рассмотрим (теоретический) случай, когда вместо выполнения полной инструкции для выполнения низкоуровневой операции компилятор перехватывает подоперацию. Примером этого является ARM с бочкообразным сдвигом: явная инструкция (т.е. добавление или что-то еще) может иметь диапазон и семантику, но подоперация может работать с немного другими ограничениями. Компилятор может использовать это до пределов, где поведение может отличаться, например, в одном случае можно установить флаги результата, а в другом — нет. Я не могу вспомнить конкретный случай, когда это имеет значение, но есть вероятность, что какая-то странная инструкция может иметь дело только с подмножествами «в противном случае нормального поведения», и компилятор может предположить, что это хорошая оптимизация, поскольку предполагается, что неопределенное поведение действительно означает неопределенное 🙂

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

Сказав все это, однако, мы должны также рассмотреть:

  1. Вы просили компилятор C99. Большинство странных архитектур (т.е. встроенных целей) не имеют компилятора C99.
  2. Большинство «крупномасштабных» разработчиков компиляторов имеют дело с очень большими базами пользовательского кода и, вообще говоря, с кошмарной поддержкой лиц, чрезмерно оптимизируя мелкие тонкости. Так они и не делают. Или они делают это так, как это делают другие игроки.
  3. В конкретном случае целого числа со знаком «неопределенное поведение» обычно дополнительная операция без знака является определенной операцией, т. Е. Я видел приведение кода к подписанному без знака только для выполнения операции, а затем приведение результата обратно.

Я думаю, что лучший прямой ответ, который я мог бы дать: «Вы можете предположить, что все это не имеет значения, но, возможно, не стоит».

1