Почему деление двух int не дает правильного значения при назначении double?

Как получилось, что в следующем фрагменте

int a = 7;
int b = 3;
double c = 0;
c = a / b;

c в конечном итоге имеет значение 2, а не 2,3333, как можно было бы ожидать. Если a и b удваиваются, ответ превращается в 2,333. Но, конечно, поскольку c уже является двойным, он должен был работать с целыми числами?

Так почему же int/int=double не работает?


person Jahoe    schedule 27.09.2011    source источник
comment
Возможный дубликат Результат деления всегда равен нулю   -  person phuclv    schedule 09.09.2018


Ответы (10)


Это связано с тем, что вы используете версию operator/ с целочисленным делением, которая занимает 2 int и возвращает int. Чтобы использовать версию double, которая возвращает double, по крайней мере один из int должен быть явно приведен к double.

c = a/(double)b;
person Chad La Guardia    schedule 27.09.2011
comment
Я бы предпочел явно преобразовать a и b в double просто для ясности, но на самом деле это не имеет значения. - person John Dibling; 27.09.2011
comment
Поскольку вопрос помечен как C++, я бы предпочел видеть static_cast‹›, а не C-приведение. - person Martin York; 27.09.2011
comment
Лично я считаю, что приведения в стиле C более четкие (приведение в большинстве других распространенных языков выполняется в стиле C). static_cast<> всегда казался мне многословным. В случае с примитивами нет никакой опасности перепутать static_cast<> и reinterpret_cast<>. - person Chad La Guardia; 27.09.2011
comment
@Tux-D: Для арифметического приведения? Я бы предпочел избегать static_cast в этом случае и вместо этого использовать приведение в стиле C. Здесь нет никакой пользы от использования приведений в стиле C++, и они загромождают код намного больше, чем приведения в стиле C. Арифметическое приведение — это именно тот контекст, в котором приведения в стиле C совершенно уместны и даже более уместны, чем другие приведения. - person AnT; 27.09.2011
comment
Иногда вы можете перехитрить людей, не использующих C-стиль, написав double(b). Они не всегда понимают, что это преобразование, поскольку оно выглядит так же, как явный вызов конструктора. - person Steve Jessop; 27.09.2011
comment
преобразование одного int в двойное работает нормально. Но почему? Я запутался в пользовательском типе. когда мы перегружаем оператор, тогда левая переменная (объект) решает, какой оператор класса вызывать. INT obj; obj/3; здесь / вызывается для класса obj. не за 3. - person Asif Mushtaq; 16.01.2016

За очень немногими исключениями (я могу вспомнить только одно) C++ определяет весь смысл выражения (или подвыражения) из самого выражения. Что вы делаете с результатами выражения, не имеет значения. В вашем случае в выражении a / b не видно double; все int. Поэтому компилятор использует целочисленное деление. Только получив результат, он решает, что с ним делать, и преобразует его в double.

person James Kanze    schedule 27.09.2011
comment
Единственное исключение, о котором я могу думать, - это выбор перегрузки функции при получении указателя - значение &funcname зависит от того, к какому типу вы его приводите. - person Steve Jessop; 27.09.2011
comment
@Steve Jessop Это единственное исключение, о котором я тоже могу думать. (Но, учитывая размер и сложность стандарта, я не хотел бы клясться, что ничего не пропустил.) - person James Kanze; 27.09.2011

Вот:

а) Деление двух int всегда выполняет целочисленное деление. Таким образом, результатом a/b в вашем случае может быть только int.

Если вы хотите сохранить a и b как int, но полностью разделить их, вы должны привести хотя бы одно из них к удвоению: (double)a/b или a/(double)b или (double)a/(double)b.

б) c — это double, поэтому он может принимать значение int при назначении: int автоматически преобразуется в double и назначается c.

c) Помните, что при присваивании выражение справа от = вычисляется сначала (в соответствии с правилом (a) выше и без учета переменной слева от =) и затем присваивается переменной слева от = (согласно пункту (b) выше). Я считаю, что это завершает картину.

person nplatis    schedule 27.09.2011

Когда вы делите два целых числа, результатом будет целое число, независимо от того, что вы сохраняете его в двойном значении.

person Alok Save    schedule 27.09.2011

c является переменной double, но присваиваемое ей значение является значением int, потому что оно является результатом деления двух int, что дает вам «целочисленное деление» (отбрасывание остатка). Итак, в строке c=a/b происходит следующее:

  1. a/b оценивается, создавая временный тип int
  2. значение временного присваивается c после преобразования в тип double.

Значение a/b определяется вне зависимости от его контекста (назначение double).

person Fred Foo    schedule 27.09.2011

В языке C++ результат подвыражения никогда не зависит от окружающего контекста (за некоторыми редкими исключениями). Это один из принципов, которому тщательно следует язык. Выражение c = a / b содержит независимое подвыражение a / b, которое интерпретируется независимо от чего-либо вне этого подвыражения. Языку все равно, что вы позже присвоите результат double. a / b — целочисленное деление. Все остальное не имеет значения. Вы увидите, что этому принципу следуют во многих уголках спецификации языка. Вот как работает C++ (и C).

Одним из примеров исключения, о котором я упоминал выше, является назначение/инициализация указателя функции в ситуациях с перегрузкой функции.

void foo(int);
void foo(double);

void (*p)(double) = &foo; // automatically selects `foo(fouble)`

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

person AnT    schedule 27.09.2011

Оператор / можно использовать для целочисленного деления или деления с плавающей запятой. Вы даете ему два целочисленных операнда, поэтому он выполняет целочисленное деление, а затем результат сохраняется в двойном.

person Vicky    schedule 27.09.2011

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

При делении двух чисел одного типа (целых, двойных и т. д.) результат всегда будет одного типа (поэтому 'int/int' всегда будет иметь результат int).

В этом случае у вас есть double var = integer result, который приводит целочисленный результат к двойному после вычисления, и в этом случае дробные данные уже потеряны. (большинство языков будут выполнять это приведение, чтобы предотвратить неточности типов, не вызывая исключения или ошибки).

Если вы хотите сохранить результат как двойной, вам нужно создать ситуацию, когда у вас есть double var = double result

Самый простой способ сделать это — заставить выражение в правой части уравнения привести к удвоению:

c = a/(double)b

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

После приведения вверх a превратится в двойника, и теперь у вас есть деление между двумя двойниками. Это создаст желаемое разделение и присвоение.

ОПЯТЬ, обратите внимание, что это зависит от языка (и может даже от компилятора), однако почти все языки (конечно, все те, которые я могу придумать навскидку) обрабатывают этот пример одинаково.

person matthewdunnam    schedule 27.09.2011
comment
Этот вопрос помечен [C++], и стандарт C++ определяет, как именно это работает. Не уверен, что вы подразумеваете под конкретным языком, и это, конечно, не зависит от компилятора, при условии, что расширения компилятора не задействованы. - person John Dibling; 27.09.2011
comment
Также неверно говорить, что результат double var = integer, который преобразует double var в int. Двойник не приводится к типу int. Результат int преобразуется в double. - person John Dibling; 27.09.2011
comment
Я допускал возможность расширений компилятора (на самом деле однажды у меня была эта проблема, когда моя среда неправильно отображала результаты, и я не мог понять, почему). И результат зависит от языка, поскольку в некоторых языках не соблюдаются одни и те же правила приведения. Я не считал, что это специальный тег C++. Вы правы насчет комментария к результату double var = integer. Отредактировано, чтобы отразить это. Спасибо! - person matthewdunnam; 28.09.2011

По тем же причинам, что и выше, вам придется преобразовать один из типов «a» или «b» в тип double. Другой способ сделать это - использовать:

double c = (a+0.0)/b;

Числитель (неявно) преобразуется в двойное число, потому что мы добавили к нему двойное число, а именно 0.0.

person Adola    schedule 05.09.2020

Важно, чтобы один из элементов расчета был типа float-double. Затем, чтобы получить двойной результат, вам нужно привести этот элемент, как показано ниже:

c = static_cast<double>(a) / b;

или c = a/static_cast(b);

Или вы можете создать его напрямую::

c = 7.0 / 3;

Обратите внимание, что один из элементов вычисления должен иметь '.0', чтобы указать на деление типа float-double на целое число. В противном случае, несмотря на то, что переменная c является двойной, результат также будет равен нулю (целое число).

person TheArchitect    schedule 06.03.2020
comment
Что дает ваш ответ, чего нет ни в одном из других 9 ответов? - person bolov; 06.03.2020