2017-02-10 18 views
4

Поля заголовка не достаточно долго, чтобы взятие подробного вопроса, так и для записи, мой фактического вопрос определяет «неразумным» определенным образом:Может ли компилятор C реализовать подписанный сдвиг вправо «необоснованно»?

Законно для C имеют арифметическое право оператор сдвига, который возвращает разные результаты, со временем, для идентичных значений аргументов? То есть, должен >> быть истинным function?

Представьте, что вы хотели написать переносимый код, используя правильный сдвиг >> подписанных значения в C. К сожалению, для вас, наиболее эффективной реализации некоторой критической части вашего алгоритма является самым быстрым, когда Подписанные правые сдвиги заполнить знаковый бит (т. е. они арифметические сдвиги вправо). Теперь, начиная с this behavior is implementation defined, вы отвратителен, если хотите написать переносимый код, который использует его.

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

Один из способов я подумал о том, чтобы просто тесте поведения компилятора во время выполнения: это он появляется для реализации арифметического сдвига вправо, использовать оптимизированный алгоритм, но если не использовать запасной вариант, который не делает полагайтесь на это. Конечно, просто проверка того, что говорят, что (short)0xFF00 >> 4 == 0xFFF0 недостаточно, так как он не исключает, что возможно char или int значения работают по-другому или даже странный случай, когда он заполняет некоторые суммы или значения сдвига, но не для других .

Так что при условии, что комплексный подход будет исчерпывающим образом проверять все значения и величины сдвига. Для всех 8-битных char входов, это только 2 значения LHS и 2 значения РИТ в общей сложности 2 , в то время как short (обычно, но скажем int16_t, если вы хотите быть педантичным) составляет только 2 , который может быть проверен на долю секунды на современном оборудовании . Выполнение всех 2 32-битные значения занимают несколько секунд на достойном оборудовании, но все же, возможно, разумны. Однако в обозримом будущем 64-битная версия.

Предположим, вы сделали это и обнаружили, что поведение >>точно соответствует желаемому арифметическому сдвигу. Безопасно ли вам полагаться на него в соответствии со стандартом: стандартно ли ограничивает реализацию , чтобы не изменять его поведение во время выполнения? То есть поведение должно быть выражено как функция его входов, или может (short)0xFF00 >> 4 быть 0xFFF0 один момент, а затем 0x0FF0 (или любое другое значение) следующего?

Сейчас этот вопрос в основном теоретический характер, но нарушают это, вероятно, не так с ума, как это может показаться, учитывая наличие гибридных архитектур, таких как big.LITTLE, динамически перемещать процессы от на CPU к другому с возможными небольшими различиями в поведении , или живая миграция VM между чипами, производимыми Intel и AMD, с незначительными (обычно недокументированными) различиями в поведении инструкций.


Под «законным» я имею в виду согласно C стандарта, не в соответствии с законодательством вашей страны, конечно.

I.e., он реплицирует бит знака для вновь сдвинутых бит.

Это было бы вид с умом, но не , что с умом: х86 имеет различное поведение (переполнение битого флага настройки) для сдвигов 1, а не другие сдвигов, а также ARM масок величины сдвига в surprising way, что простой тест, вероятно, не обнаружит.

По крайней мере, ничего лучше микроконтроллера. Внутренний цикл проверки представляет собой несколько простых инструкций, поэтому процессор с тактовой частотой 1 ГГц может проверять все ~ 1 млн коротких значений в ~ 1 мс при одной инструкции за цикл.

+0

Итак, вы в основном задаете вопрос, является ли поведение, определяемое реализацией, последовательным во всей реализации? Я бы сказал, это зависит. I случай * чистых * математических функций, таких как сдвиги, они не должны давать * никаких * побочных эффектов, и их результат должен зависеть только от значений операндов. В случае некоторых других функций без ограничения * чистоты, это может быть иначе .. –

+0

В качестве побочного примечания ваше тестовое выражение с «коротким» является ошибочным. Все операции будут по крайней мере использовать тип, такой же широкий, как «int» в качестве базового типа. Приведение к «короткому», а затем перемещение ничего не покупает, оно по-прежнему «int». –

+0

Более или менее это то, что я прошу - хотя мое точное состояние было даже немного более узким - может ли значение измениться в течение всего жизненного цикла одного процесса. Обратите внимание, что побочные эффекты не нужны для изменения поведения. Сам '>>' может быть без побочных эффектов, но что-то другое может измениться (не связано с каким-либо вызовом '>>'), которое влияет на последующие вызовы (я полагаю ...). – BeeOnRope

ответ

3

Предположим, вы это сделали и обнаружили, что поведение >> точно соответствует желаемому арифметическому сдвигу. Безопасно ли вам полагаться на него в соответствии со стандартом: стандартизирует ли это выполнение, чтобы не изменять его поведение во время выполнения? То есть поведение должно быть выражено как функция его входов, или может (short)0xFF00 >> 4 быть 0xFFF0 один момент, а затем 0x0FF0 (или любое другое значение) следующего?

В стандарте не установлены требования к форме или характеру (обязательного) определения поведения смещения отрицательного целого числа. В частности, он не запрещает определение быть условным по свойствам компиляции или времени выполнения за пределами операндов. В самом деле, это имеет место для поведения, определяемого реализацией в целом. Стандарт defines the term просто

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

Так, например, реализация может обеспечить макрос, глобальную переменную или функцию, позволяющую выбирать между арифметическими и логическими сдвигами. Реализации могут также определять смещение вправо отрицательного числа, чтобы сделать менее правдоподобным или даже диким im правдоподобными вещами.

Тестирование поведения - разумный подход, но он дает вам только вероятностный ответ. Тем не менее, на практике я считаю, что довольно легко выполнить лишь небольшое количество тестов - может быть, всего лишь одно - для каждого интересующего типа данных LHS. Очень маловероятно, что вы столкнетесь с реализацией, реализующей что-либо, кроме стандартного арифметического (всегда) или логического (всегда) правого сдвига, или что вы столкнетесь с тем, где выбор между арифметическими и логическими сдвигами изменяется для разных операндов тот же (продвинутый) тип.

+0

Есть некоторые аспекты реализации, определенные при реализации, которые должны быть документированы и не должны меняться для конкретной реализации. См. Приложение J.3. Для других это разделение не требуется, например. bitshifts. – Olaf

+1

@Olaf, «каждый документ реализации, как делается выбор», является частью определения * стандарта * для «реализации». Все поведение, определяемое реализацией, должно быть документировано. Приложение J является информативным, а не нормативным - оно не может (не может) ограничивать поведение, определяемое реализацией, которое должно быть документировано. –

+0

@JohnBollinger - как насчет исчерпывающего тестирования? Я согласен со всем, что вы сказали, и тестирование некоторых аргументов дает вам вероятностный ответ - но вы, похоже, подразумеваете, что тестирования каждого аргумента будет достаточно, так как вы полностью наметили поведение реализации. Мой вопрос на самом деле: даже если вы делаете _that_, может ли реализация просто _define_ что значение '>>' может меняться со временем с одинаковыми аргументами? – BeeOnRope

3

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

Стандарт несколько расплывчатый относительно уровня точности, с которым должно быть документировано поведение «Выполнено». Я думаю, что намерение состоит в том, что человек разумного интеллекта, читающий спецификацию, должен уметь предсказать, как будет вести себя кусок кода с поведением, определяемым реализацией, но я не уверен, что определение операции как «уступки всему, что происходит в регистр 7 "будет несоответствием.

С практической точки зрения, важно иметь в виду, является ли таргетинг на качественные реализации платформ, которые были разработаны в течение последней четверти века, или же кто-то пытается заставить даже сознательно-тупые компиляторы вести себя контролируемым образом. Если первое, то может стоить с использованием статического утверждения, чтобы обеспечить -1 >> 1 == - 1, но качественные компиляторы для современного оборудования , где этот тест пройдет, будет использовать арифметическую сдвиг вправо последовательно. В то время как код таргетинга для тупых компиляторов может иметь некоторую цель , как правило, невозможно защищать от всех способов, которыми компилятор, имеющий патологический характер, может саботировать свой код, а усилия , потраченные на это, часто могут быть более эффективными потраченный на получение компилятора качества .

 Смежные вопросы

  • Нет связанных вопросов^_^