Целое или плавающее деление -> Кто отвечает за результат?

Я некоторое время программировал на C++, но вдруг у меня возникли сомнения, и я хотел уточнить у сообщества Stackoverflow.

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

Но кто несет ответственность за обеспечение этого результата? Это компилятор или инструкция DIV?


person nsivakr    schedule 15.08.2010    source источник
comment
Вы делите константы или переменные?   -  person James Black    schedule 15.08.2010


Ответы (6)


Компилятор решит во время компиляции, какая форма деления требуется, исходя из типов используемых переменных - в конце дня будет задействована инструкция DIV (или FDIV) той или иной формы.

person Will A    schedule 15.08.2010
comment
Если переменные не являются константами времени компиляции, в этом случае компилятор берет на себя ответственность, и инструкции не создаются. - person Potatoswatter; 16.08.2010
comment
@Potatoswatter: не совсем так; компилятор ДОЛЖЕН оценивать интегральные константные выражения и МОЖЕТ оценивать другие константные выражения. т.е. float x = 1.0/3.0 может быть оценен компилятором, или это может привести к FDIV - выбору компилятора. - person MSalters; 16.08.2010

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

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

person Carl Norum    schedule 15.08.2010

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

Компилятор отвечает за построение любой операции, написанной в исходном коде, поверх этих примитивов. Например, в C, если вы разделите число с плавающей запятой на целое число, компилятор выдаст преобразование типа int в число с плавающей запятой, а затем деление с плавающей запятой.

(Дурацкие исключения действительно существуют. Я не знаю, но я бы не стал ставить дальше VAX наличие инструкций типа «разделить число с плавающей точкой на целое число». В Itanium на самом деле не было инструкции деления, но его «помощник " был только для чисел с плавающей запятой, вам пришлось имитировать целочисленное деление поверх деления с плавающей запятой!)

person zwol    schedule 15.08.2010
comment
Хе. Что касается дурацких исключений, как вы их называете, вы, вероятно, только что использовали одно из них, чтобы написать этот ответ. Инструкция x86 (или, точнее, x87) для разделения числа с плавающей запятой на целое называется FIDIV. - person slacker; 15.08.2010
comment
И это не единственный пункт, где дурацкость x86 посрамляет VAX :). - person slacker; 15.08.2010
comment
Ха! Ну, это будет x87. Они не делали ничего как обычно с этой штукой. - person zwol; 15.08.2010

Ваш вопрос не имеет особого смысла. Инструкция DIV сама по себе ничего не делает. Как бы вы на него ни кричали, даже если вы пытаетесь его подкупить, он не берет на себя ответственность ни за что

Когда вы программируете на языке программирования [X], компилятор [X] несет исключительную ответственность за создание программы, которая делает то, что вы описали в исходном коде.

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

Но это всегда зависит от компилятора. Ваша программа C++ не имеет какого-либо эффекта, если она не интерпретируется в соответствии со стандартом C++. Если вы интерпретируете его как обычный текстовый файл, он ничего не делает. Если ваш компилятор интерпретирует это как программу Java, он задохнется и отклонит ее.

А инструкция DIV ничего не знает о стандарте C++. Компилятор C++, с другой стороны, написан с единственной целью — понять стандарт C++ и преобразовать код в соответствии с ним.

Компилятор всегда несет ответственность.

person jalf    schedule 15.08.2010

Одним из наиболее важных правил стандарта C++ является правило «как если бы»:

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

Что в отношении вашего вопроса означает, что не имеет значения, какой компонент выполняет разделение, пока оно выполняется. Это может быть выполнено DIV машинным кодом, это может быть выполнено более сложным кодом, если нет соответствующей инструкции для рассматриваемого процессора.

Он также может:

  1. Замените операцию битовым сдвигом, если это уместно и, вероятно, будет быстрее.
  2. Замените операцию литералом, если она вычислима во время компиляции, или присваиванием, если, например. при обработке x/y во время компиляции можно показать, что y всегда будет 1.
  3. Замените операцию броском исключения, если во время компиляции можно показать, что это всегда будет целочисленное деление на ноль.
person Jon Hanna    schedule 15.08.2010
comment
Может ли он отказаться от компиляции, если во время компиляции он может сделать вывод, что частное всегда будет равно нулю, или допустимо ли, чтобы полностью соответствующая программа на C содержала инструкцию типа x=1/(x-x); при условии, что такая инструкция никогда не выполняется? - person supercat; 15.08.2010
comment
@supercat: Это совершенно законно. Это выполнение деления на ноль, вызывающее неопределенное поведение. - person slacker; 15.08.2010
comment
@supercat, если вы хотите создать программу, которая всегда ошибается, это ваше дело! В конце концов, трудно учить людей ошибкам без них (хотя в некоторых языках вам нужно делать такие вещи, как if(true == false), чтобы обмануть компиляторы и позволить вам скомпилировать). Безусловно, было бы разумно и полезно, если бы компилятор выдал нестандартное предупреждение. - person Jon Hanna; 16.08.2010
comment
... важно то, что если непогрешимый знаток языка говорит, что он будет вести себя таким образом, то он должен делать то, что говорит знаток, как видно извне. Как это сделать, зависит от компилятора. Те биты, которые не определены, - это когда эксперт скажет, что я не знаю, что он будет делать (на самом деле он, вероятно, будет разглагольствовать о плохом коде, вы знаете, на что похожи эксперты), но определено применяется только к внешним взглядам. Это важно для обеспечения оптимизации, и действительно, большая часть STL зависит от этого, чтобы не сильно снижать производительность. - person Jon Hanna; 16.08.2010
comment
Компилятор также может заменить x/2.5 на x * 0.4. На самом деле это обычное дело, поскольку умножение часто происходит быстрее, чем деление. - person MSalters; 16.08.2010

Практически

Стандарт C99 определяет, что при делении целых чисел результатом оператора / является алгебраическое частное с отбрасыванием любой дробной части. И добавляет в сноске, что это часто называют «усечением до нуля».

История

Исторически так сложилось, что спецификация языка является ответственной.

Паскаль определяет свои операторы так что использование / для деления всегда возвращает real (даже если вы используете его для деления 2 целых чисел), и если вы хотите разделить целые числа и получить целочисленный результат, вы используете вместо этого оператор div. (Visual Basic имеет аналогичное отличие и использует оператор \ для целочисленного деления, который возвращает целочисленный результат.)

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

person Ken Bloom    schedule 15.08.2010