На каких платформах целое деление на ноль запускает исключение с плавающей запятой?

В другом вопросе кто-то задавался вопросом, почему они получают «ошибку с плавающей запятой», когда на самом деле у них в своём C ++-программе было целое число деление на ноль. Вокруг этого возникло обсуждение, причем некоторые утверждают, что исключения с плавающей запятой на самом деле никогда не поднимаются для деления на float на ноль, а возникают только при делении на целое число на ноль.

Это звучит странно для меня, потому что я знаю это:

  1. MSVC-скомпилированный код на x86 и x64 на всех платформах Windows сообщает, что int делит на ноль как «0xc0000094: целочисленное деление на ноль», а float делит на ноль как 0xC000008E «Деление с плавающей запятой на ноль» (когда включено)

  2. IA-32 и AMD64 ISA определяют #DE (исключение целочисленного деления) как прерывание 0. Исключения с плавающей запятой запускают прерывание 16 (x87 с плавающей запятой) или прерывание 19 (плавающая точка SIMD).

  3. Другие аппаратные средства имеют аналогичные разные прерывания ( например, PPC повышает 0x7000 на float-div-by-zero и вообще не ловушка для int / 0).

  4. Наше приложение исключает исключения с плавающей запятой для деления на ноль с встроенным _controlfp_s (в конечном счете stmxcsr op), а затем захватывает их для целей отладки. Поэтому я определенно видел исключения на основе IEEE754 на основе исключения на практике.

Поэтому я пришел к выводу, что есть некоторые платформы, которые сообщают об исключениях в качестве исключений с плавающей запятой, таких как x64 Linux (повышение SIGFPE для всех арифметических ошибок независимо от канала ALU) .

Какие другие операционные системы (или время выполнения C / C ++, если вы являетесь операционной системой) представляют целочисленное значение div-by-zero как исключение с плавающей запятой?

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

Тем не менее, вы можете разобраться, какой именно SIGFPE он был, чтобы убедиться, что это фактически исключение разделения. (Не обязательно делить на ноль, хотя: 2 дополнения INT_MIN / -1 ловушки деления, а div и idiv также idiv когда частное деление 64b / 32b не подходит в выходном регистре 32b.)

В руководстве glibc объясняется, что BSD и GNU-системы предоставляют дополнительный аргумент для обработчика сигналов для SIGFPE , который будет FPE_INTDIV_TRAP для деления на ноль. POSIX документы FPE_INTDIV_TRAP как возможное значение для siginfo_t s int si_code , в системах, где siginfo_t включает этот элемент.

IDK, если Windows поставляет другое исключение в первую очередь или связывает вещи с разными вариантами того же арифметического исключения, что и Unix. Если это так, обработчик по умолчанию расшифровывает дополнительную информацию, чтобы сообщить вам, какое это было исключение.

POSIX и Windows используют фразу «деление на ноль» для охвата всех исключений целочисленного деления, поэтому, по-видимому, это общая сокращенная версия. Для людей, которые знают о том, что INT_MIN / -1 (с дополнением 2) является проблемой, фразу «деление на ноль» можно принять за синоним исключения. Эта фраза сразу же указывает на общий случай для людей, которые не знают, почему целочисленное деление может быть проблемой.


Семантика исключений FP

Исключения FP маскируются по умолчанию для процессов пользовательского пространства в большинстве операционных систем / C ABI.

Это имеет смысл, поскольку плавающая точка IEEE может представлять бесконечности и имеет NaN для распространения ошибки на все будущие вычисления с использованием значения.

  • 0.0/0.0 => NaN
  • Если x конечен: x/0.0 => +/-Inf со знаком x

Это даже позволяет таким вещам создавать разумный результат, когда исключения маскируются:

 double x = 0.0; double y = 1.0/x; // y = +Inf double z = 1.0/y; // z = 1/Inf = 0.0, no FP exception 

Обнаружение FP и целых чисел

Способ обнаружения ошибок FP довольно хорош: когда исключения маскируются, они устанавливают флаг в регистре состояния FP вместо улавливания. (например, MXCSR x86 для инструкций SSE). Флаг остается установленным до тех пор, пока он не будет очищен вручную, поэтому вы можете проверить один раз (после цикла, например), чтобы увидеть, какие исключения произошли, но не там, где они произошли.

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

Однако на x86, обнаружив, что при переполнении целых чисел во время последовательности вычислений требуется поместить условную ветвь после каждого из них, поскольку флаги просто перезаписаны. В MIPS есть команда add которая будет ловушка при подписанном переполнении и неподписанная инструкция, которая никогда не ловушки. Таким образом, целочисленное обнаружение и обработка исключений намного стандартизировано.


Целочисленное подразделение не имеет возможности производить результаты NaN или Inf, поэтому имеет смысл работать таким образом .

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

Однако на x86 преобразование значения с плавающей запятой вне диапазона с целым числом с cvtsd2si или любой аналогичной командой преобразования приводит к «целочисленному неопределенному» значению, если исключение «с плавающей запятой недействительно» замаскировано. Значение равно нулю, кроме знакового бита. т.е. INT_MIN .

(См. Руководства Intel, ссылки в вики-теге x86 .

Какие другие операционные системы (или время выполнения C / C ++, если вы являетесь операционной системой) представляют целочисленное значение div-by-zero как исключение с плавающей запятой?

Ответ зависит от того, находитесь ли вы в пространстве ядра или в пространстве пользователя . Если вы находитесь в пространстве ядра, вы можете поместить «i / 0» в kernel_main() , обработчик прерываний вызовет обработчик исключений и остановит ваше kernel. Если вы находитесь в пространстве пользователя, ответ зависит от настроек вашей операционной системы и компилятора.

Аппаратное обеспечение AMD64 определяет целочисленное деление на ноль в качестве прерывания 0, отличное от прерывания 16 (исключение с плавающей точкой x87) и прерывание 19 (исключение с плавающей запятой SIMD).

Исключением «Разделить на ноль» является деление на ноль с помощью команды div . Обсуждение FPU x87 выходит за frameworks этого вопроса.

Другие аппаратные средства имеют аналогичные разные прерывания (например, PPC повышает 0x7000 на float-div-by-zero и вообще не ловушка для int / 0).

Более конкретно, 00700 сопоставляется с типом исключения «Программа», который включает исключение с поддержкой с плавающей запятой. Такое исключение возникает при попытке деления на ноль с помощью команды с плавающей запятой.

С другой стороны, целочисленное деление является неопределенным поведением на PPC PEM:

8-53 divw

Если делается попытка выполнить любое из делений -0x8000_0000 ÷ -1 или ÷ 0, то содержимое rD не определено, равно как и содержимое бит LT, GT и EQ поля CR0 (если Rc = 1 ). В этом случае, если OE = 1, то устанавливается OV.

Наше приложение исключает исключения с плавающей запятой для деления на ноль с встроенным _controlfp_s (в конечном счете stmxcsr op), а затем захватывает их для целей отладки. Поэтому я определенно видел исключения на основе IEEE754 на основе исключения на практике.

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

Для userpace это происходит в AIX, работающем на POWER, HP-UX, работающем на PA-RISC, Linux, работающем на x86-64, macOS, работающем на x86-64, Tru64, работающем на Alpha и Solaris, запущенном на SPARC.

Во избежание деления на ноль во время компиляции намного лучше.

  • Как реализовать правильную обработку ошибок HTTP в .NET MVC 2?
  • Google Analytics Google Analytics Ошибка 403: «У пользователя нет учетной записи Google Analytics»
  • Как получить полезные сообщения об ошибках в PHP?
  • Обработка ошибок ASP.NET MVC 404
  • целочисленная проверка ввода, как?
  • Обработка ошибок ASP.NET MVC 5
  • Обработка ошибок в коде C
  • обрабатывать ajax-ошибку, когда пользователь нажимает кнопку обновления
  • Eclipse возвращает сообщение об ошибке «Java был запущен, но возвратил код выхода = 1»
  • Interesting Posts

    Home Server: виртуализация серверов, что выбрать?

    Несинхронизированные статические методы streamобезопасны, если они не изменяют переменные статического classа?

    Не удается удалить надстройку Internet Explorer

    Как конвертировать UTF-8 std :: string в UTF-16 std :: wstring?

    Как вы конвертируете дату POSIX в день года в R?

    Как настроить IIS для приема запросов POST?

    Добавить пункт меню в контекстное меню Windows только для определенного типа файла

    angular2 вручную активировать событие щелчка на конкретном элементе

    Новая панель навигации нижнего уровня Android или BottomNavigationView

    завершить программный вызов

    Установить цвет области TextView в Android

    Загрузка виртуального бокса из раздела – несовместимая ошибка лицензии

    Не запрашивать проблему Window.FEATURE_ACTION_BAR

    Что можно безопасно удалить из папки «C: \ WINDOWS»?

    Несколько пользовательских моделей с Ruby On Rails и разработайте отдельные маршруты регистрации, но один общий логин

    Давайте будем гением компьютера.