gcc — C ++ предупреждение: деление двойного на ноль

Случай 1:

#include <iostream>

int main()
{
double d = 15.50;
std::cout<<(d/0.0)<<std::endl;
}

Компилируется без каких-либо предупреждений и распечаток inf, ОК, C ++ может обрабатывать деление на ноль, (увидеть это в прямом эфире).

Но,

Случай 2:

#include <iostream>

int main()
{
double d = 15.50;
std::cout<<(d/0)<<std::endl;
}

Компилятор выдает следующее предупреждение (увидеть это в прямом эфире):

warning: division by zero [-Wdiv-by-zero]
std::cout<<(d/0)<<std::endl;

Почему компилятор выдает предупреждение во втором случае?

Является 0 != 0.0?

Редактировать:

#include <iostream>

int main()
{
if(0 == 0.0)
std::cout<<"Same"<<std::endl;
else
std::cout<<"Not same"<<std::endl;
}

выход:

Same

97

Решение

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

Для целых чисел нет способа представить бесконечность, и язык определяет операцию для неопределенное поведение так что компилятор старательно пытается увести вас с этого пути.

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

107

Другие решения

В стандарте C ++ оба случая неопределенное поведение. Все может произойти, в том числе форматирование вашего жесткого диска. Вы не должны ожидать или полагаться на «return inf. Ok» или любое другое поведение.

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

Из стандарта C ++ 17 [expr.mul] / 4:

Бинарный / оператор дает частное, а двоичный % Оператор возвращает остаток от деления первого выражения на второе. Если второй операнд / или же % ноль, поведение не определено.

42

Моя лучшая догадка, чтобы ответить этот конкретный вопрос было бы, что компилятор выдает предупреждение до выполняя преобразование int в double,

Итак, шаги будут выглядеть так:

  1. Разбор выражения
  2. Арифметический оператор /(T, T2), где T=double, T2=int,
  3. Проверь это std::is_integral<T2>::value является true а также b == 0 — это вызывает предупреждение.
  4. Испускать предупреждение
  5. Выполнить неявное преобразование T2 в double
  6. Выполните четко определенное деление (поскольку компилятор решил использовать IEEE 754).

Это, конечно, предположение и основано на спецификациях, определенных компилятором. Со стандартной точки зрения мы имеем дело с возможными неопределенными поведениями.


Обратите внимание, что это ожидаемое поведение в соответствии с Документация GCC
(кстати, кажется, что этот флаг нельзя использовать явно в GCC 8.1)

-WDiv на ноль
Предупреждать о целочисленном делении во время компиляции на ноль. Это по умолчанию. Чтобы запретить предупреждающие сообщения, используйте -Wno-div-by-zero. Плавающая запятая
о делении на ноль не предупреждают, так как это может быть законным способом
получение бесконечностей и NaNs.

12

Я не буду вдаваться в разгром UB / не UB в этом ответе.

Я просто хочу указать, что 0 а также 0.0 разные несмотря на 0 == 0.0 оценивая к истине. 0 является int буквальный и 0.0 это double буквальный.

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

9

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

Является ли любой из этих двух UB не имеет значения. Цитируя стандарт:

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

(Акцент мой)

Рассмотреть «предложить круглые скобки вокруг назначения, используемого в качестве значения истины«предупреждение: способ сообщить компилятору, что вы действительно Я хочу использовать результат присваивания, будучи явным и добавляя круглые скобки вокруг присваивания. Результирующий оператор имеет тот же эффект, но он говорит компилятору, что вы знаете, что делаете. То же самое можно сказать и о foo/0.0: Поскольку вы явно указываете компилятору «Это деление с плавающей запятой», используя 0.0 вместо 0компилятор доверяет вам и не выдаст предупреждение.

7

Это похоже на ошибку GCC, документация для -Wno-div-by-zero ясно говорит:

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

и после Обычные арифметические преобразования покрыт [Expr.arith.conv] оба операнда будут двойной:

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

В противном случае, если один из операндов является двойным, другой должен быть преобразован в двойной.

а также [Expr.mul]:

Операнды * и / должны иметь арифметический или незаданный тип перечисления; операнды% должны иметь целочисленный или незаданный тип перечисления.
Обычные арифметические преобразования выполняются над операндами и определяют тип результата.

Относительно того, является ли деление с плавающей точкой на ноль неопределенным поведением, и как выглядит реализация с ним мой ответ здесь. TL; DR; Похоже, GCC соответствует Приложение F по делению с плавающей точкой на ноль, поэтому undefined здесь не играет роли. Ответ был бы другим для лязга.

4

Деление с плавающей точкой на ноль ведет себя иначе, чем целочисленное деление на ноль.

IEEE с плавающей точкой стандарт различает + inf и -inf, в то время как целые числа не могут хранить бесконечность. Целочисленное деление на ноль приводит к неопределенному поведению. Деление с плавающей точкой на ноль определяется стандартом с плавающей точкой и приводит к + inf или -inf.

2
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector