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

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

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

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

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

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

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

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

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

Ответ 1

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

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

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

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

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


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

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

Это имеет смысл, поскольку плавающая точка 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 вместо перехвата. (например, x86 MXCSR для инструкций SSE). Флаг остается установленным до тех пор, пока не будет сброшен вручную, поэтому вы можете проверить один раз (например, после цикла), чтобы увидеть, какие исключения произошли, а не где они произошли.

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

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


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

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

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

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

Ответ 2

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

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

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

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

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

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

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

8-53 divw

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

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

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

Ответ 3

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

Уклонение от деления на ноль во время компиляции намного лучше.