Будет ли компьютер пытаться делить на ноль?

59

Мы все знаем, 0/0есть Undefinedи возвращает ошибку, если бы я поместил ее в калькулятор, и если бы я создал программу (по крайней мере, на C), ОС прервала бы ее, когда я попытался бы разделить на ноль.

Но что меня интересует, так это то, что компьютер даже пытается делить на ноль или он просто имеет «встроенную защиту», так что, когда он «видит», 0/0он возвращает ошибку даже до попытки его вычисления?

Ankush
источник
10
0/0 не определено, любое другое число / 0 - это ошибка другого типа, вы, кажется,
путаете
7
Математически говоря, любое число, деленное на 0, не определено.
ManoDestra
12
@jwg: «если бы оно имело значение, оно было бы 1» - не обязательно; Есть целые
разделы
11
Для пояснения терминологии здесь 0/0 называется неопределенной формой, а x / 0 для ненулевого x не определено . Вычисление, которое заканчивается 0/0, часто может быть рассчитано по-другому, чтобы дать реальный ответ, тогда как x / 0 по сути бессмысленно.
Эра
9
@jwg тогда тебя может заинтересовать правило l'hopital. Определенно есть случаи, когда 0/0 не подразумевает значение 1.
d0nut

Ответы:

74

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

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

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

Тот же механизм прерывания и исключения будет также использоваться при включении обнаружения переполнения, которое вы обычно запрашиваете, используя различные инструкции add / sub / mul (или флаг этих инструкций).

Деление с плавающей запятой также имеет встроенное обнаружение деления на ноль, но возвращает другое значение ( IEEE 754 указывает NaN ) вместо перехвата в обработчик исключений.


Гипотетически говоря, если процессор пропустил какое-либо обнаружение для попытки деления на ноль, проблемы могут включать:

  • зависание ЦП (например, в цикле инф.) - это может произойти, если ЦП использует алгоритм деления, который останавливается, когда числитель меньше делителя (в абсолютном значении). Зависание, подобное этому, в значительной степени будет рассматриваться как сбой процессора.
  • (возможно, предсказуемый) мусорный ответ, если ЦП использует счетчик для завершения деления на максимально возможном количестве шагов деления (например, 31 или 32 на 32-битной машине).
Эрик Эйдт
источник
51
@Ankush «Сбой системы» на самом деле не то, что происходит на уровне процессора. Наиболее вероятное поведение процессора, который не улавливает деление на ноль, состоит в том, что он просто выполняет операцию деления, производит какой-то бессмысленный результат и продолжает работать. Также как когда вы добавляете два целых числа, которые переполняются.
Ixrec
6
Для плавающей запятой выбор между «NaN» и «trap» обычно устанавливается путем переключения флагов в FPU.
Ватин
10
@Ankush В случае, если то, что сказал lxrec, было неясно: что касается процессора, то такой вещи, как сбой, не существует.
user253751 26.02.16
7
@Ankush Немногие ситуации вызывают «системный сбой» на уровне процессора. В этих случаях речь идет о таких вещах, как отключение тепловой защиты (защита от перегрева), тройные неисправности и аналогичные ситуации. Почти каждый сбой, с которым вы столкнетесь при обычном использовании, включая недействительные коды операций , обрабатывается путем перехвата и восстановления каким-либо образом или просто игнорирования ошибки и продолжения выполнения в потенциально нечистом состоянии, возможно, с установленным флагом ошибки.
CVn
8
Если вы не улавливаете деление на ноль, результатом будет, на языке программирования, «Неопределенное поведение». Это означает, что компьютер может делать все что угодно. Он может, как упоминает Берги, войти в багги-петлю и зависнуть. Он может просто выводить некоторую неопределенную серию битов, которая, как оказалось, была результатом их реализации логики деления (помните, что компьютер не «делит» в математическом смысле. Он выполняет операцию над двумя числами, которая достаточно близка к делению что мы обычно называем это «делением» (см. также округление с плавающей запятой).
Корт Аммон
34

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

Для числа с плавающей запятой большинство реализаций используют стандарт IEEE 754 , где деление на 0 четко определено. 0/0 дает четко определенный результат NaN (не число), а x / 0 для x ≠ 0 дает либо + Infinity, либо -Infinity, в зависимости от знака x.

В таких языках, как C, C ++ и т. Д. Деление на ноль вызывает неопределенное поведение. Таким образом, согласно определению языка, все может произойти. Особенно то, чего ты не хочешь. Как и все отлично работает, когда вы пишете код и уничтожаете данные, когда его использует ваш клиент. Так что с языковой точки зрения не делайте этого . Некоторые языки гарантируют, что ваше приложение потерпит крах; это зависит от них, как это реализовано. Для этих языков деление на ноль приведет к сбою.

Многие процессоры имеют какую-то встроенную инструкцию «делить», которая будет вести себя по-разному в зависимости от процессора. На 32-битных и 64-битных процессорах Intel инструкции «делить» приводят к сбою приложения при попытке деления на ноль. Другие процессоры могут вести себя по-другому.

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

gnasher729
источник
22
«На 32-битных и 64-битных процессорах Intel инструкции« делить »приводят к сбою приложения при попытке деления на ноль». Нужна цитата. Процессоры не имеют никакого представления о приложениях, они выполняют инструкции и (если мы включаем MMU) устанавливают ограничения на доступ к памяти (если только не в кольце 0 или эквивалент в архитектурах не-Intel-x86). То, что инструкция является частью приложения A, а не приложения B или компонента C операционной системы, не имеет отношения к ЦП; может ли инструкция быть инструкцией X или использовать адрес памяти Y, имеет значение.
CVn
1
Чтобы добавить комментарий @ MichaelKjörling: в ОС есть способы уведомления об этом (и других видах ошибок). В мире окон это EXCEPTION_INT_DIVIDE_BY_ZEROзначение в EXCEPTION_RECORDкотором будет обрабатываться (надеюсь) установлен изготовленном Exception Handling Handler
user45891
1
Иногда они делают что-то, кроме гарантии того, что ваше приложение будет аварийно завершено. Например, многие языки / платформы гарантируют, что они сгенерируют исключение при делении на ноль. Затем вы можете поймать и обработать указанное исключение без сбоев.
Рейраб
2
Вы можете удалить «может» внутри «Другие процессоры могут вести себя по-другому»: на платформе PowerPC деление просто дает нулевой результат при делении на ноль. Что гораздо полезнее, чем паническое поведение платформы X86.
Мастер
13

Похоже, вам интересно, что произойдет, если кто-то создаст процессор, который явно не проверяет ноль перед делением. Что произойдет, полностью зависит от осуществления деления. Не вдаваясь в подробности, один вид реализации даст результат, в котором установлены все биты, например 65535 на 16-битном процессоре. Другой может повесить трубку.

Бинго
источник
1

Но что меня интересует, так это то, что компьютер даже пытается делить на ноль или он просто имеет «встроенную защиту», так что, когда он «видит» 0/0, он возвращает ошибку даже до попытки вычислить его?

Поскольку x/0бессмысленно, точка зрения, компьютеры всегда должны проверять деление на ноль. Здесь есть проблема: программисты хотят вычислять (a+b)/cбез необходимости проверять, имеет ли этот расчет смысл. Внутренний ответ на деление на ноль ЦП + тип числа + операционная система + язык заключается в том, чтобы либо сделать что-то довольно радикальное (например, аварийно завершить программу), либо сделать что-то слишком мягкое (например, создать значение, которое не смысл, такой как IEEE с плавающей точкой NaN, число, которое "не число").

В обычных условиях программист должен знать, (a+b)/cимеет ли смысл. В этом контексте нет причин проверять деление на ноль. Если происходит деление на ноль, и если машинный язык + язык реализации + тип данных + ответ операционной системы на это должен привести к аварийному завершению программы, это нормально. Если ответ заключается в создании значения, которое в конечном итоге может загрязнить каждое число в программе, это тоже нормально.

Ни «что-то радикальное», ни «слишком мягкое» не является правильным решением в мире высоконадежных вычислений. Эти ответы по умолчанию могут убить пациента, разбить авиалайнер или взорвать бомбу не в том месте. В среде с высокой надежностью программист, который пишет, (a+b)/cбудет забран до смерти во время проверки кода, или в наше время, возможно, будет забран автоматически до смерти инструментом, который проверяет верботные конструкции. В этой среде этот программист должен вместо этого написать что-то вроде div(add(a,b),c)(и, возможно, некоторую проверку состояния ошибки). Под капотом div(и также add) функции / макросы защищают от деления на ноль (или переполнения в случае add). То, что влечет за собой эта защита, зависит от конкретной реализации.

Дэвид Хаммен
источник
То, что NaN не подчиняется арифметике, которую вы изучали в школе, не означает, что в этом нет смысла. Это подчиняется другой арифметике
Caleth
-2

Мы уже знаем это x/0и 0/0не имеем четко определенных ответов. Что произойдет, если вы попытаетесь 0/0все равно рассчитать ?

В современной системе вычисление передается в MPU внутри CPU и помечается как недопустимая операция, возвращаясь NaN.

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

  • Вычитайте все меньшие и меньшие копии делителя, пока значение не достигнет нуля, и проследите, какие копии размера были вычтены
    • Если он проверяет ноль перед первым вычитанием, он быстро завершится и результат будет 0
    • Если предполагается, что он должен быть в состоянии вычесть хотя бы один раз, результат будет 1
  • Вычислите логарифмы обоих чисел, вычтите их и увеличьте e до степени результата. Очень неэффективный метод по сравнению с вышеупомянутым методом вычитания, но математически действительный
    • При попытке расчета может произойти переполнение, log(0)и программа либо использует свои процедуры обработки ошибок, либо вылетает
    • Программное обеспечение может предполагать, что все логарифмы могут быть рассчитаны с фиксированным числом шагов и возвращать большое, но неправильное значение. Поскольку оба логарифма будут одинаковыми, разница будет 0и e 0 = 1, что дает результат1

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

CJ Деннис
источник
3
Этот вопрос касается деления на ноль в целых числах, а в целых числах такого понятия не существует NaN.
Дэвид Хаммен,
3
Никакой процессор не собирается вычислять журналы и вычитать, чтобы вычислить результат деления. Время команды логарифма на несколько порядков больше, чем деление. Каково значение log (0)?
Wallyk
1
@DavidHammen ОП ни разу не упомянул целые числа, и никто из них не прокомментировал этот вопрос. Целые числа упоминаются только в ответах.
CJ Деннис
@wallyk Я уже сказал в своем ответе, что логарифмы very inefficientдля деления. Стоимость арифметики (сложение = вычитание) <= умножение <= деление. Если у вас нет MPU, который может выполнять деление за то же количество тактов, что и сложение (обычно один), тогда деление обходится дороже, чем сложение и вычитание, и, как правило, дороже, чем умножение.
CJ Деннис