Чому поділ на два int не дає потрібного значення, якщо його присвоїти подвійному?


110

Як це зробити в наступному фрагменті

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

cв кінцевому підсумку має значення 2, а не 2,3333, як можна було б очікувати. Якщо aі bє парними, відповідь перетворюється на 2.333. Але напевно, тому що c вже є двійником, він мав би працювати з цілими числами?

Так як int/int=doubleце не працює?


Відповіді:


161

Це тому, що ви використовуєте цілочисельну версію поділу operator/, яка займає 2 intс і повертає int. Для того, щоб використовувати doubleверсію, яка повертає a double, принаймні один з ints повинен бути явно зафіксований в a double.

c = a/(double)b;

9
Я волів би , щоб явно перетворити як aі bв doubleпросто для ясності, але це на самому справі не має значення.
Джон Дайлінг

31
Оскільки питання позначено тегом C ++, я вважаю за краще бачити static_cast <>, а не C.
Мартін Йорк

16
Особисто я вважаю, що ролі в стилі C чіткіші (кастинг у більшості інших поширених мов робиться способом у стилі C). static_cast<>завжди здавався мені довго звивистим. Що стосується примітивів, то насправді немає жодної небезпеки потрапити static_cast<>і reinterpret_cast<>змішатись.
Чад Ла Гуардія

6
@ Tux-D: Для арифметичних ролей? Я вважаю за краще уникати static_castв цьому випадку і використовувати замість цього стилю C. Тут немає користі у використанні ролях у стилі C ++, і вони захаращують код набагато більше, ніж касти у стилі C. Арифметичний акторський склад - це саме той контекст, коли ролі в стилі C цілком доречні і насправді більш підходящі, ніж інші ролі.
ANT

19
Іноді ви можете перехитрити людей, які не мають стилю C-style double(b). Вони не завжди усвідомлюють, що це перетворення, оскільки це виглядає так само, як явний виклик конструктора.
Стів Джессоп

12

Ось:

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

Якщо ви хочете зберегти aі bяк ints, але розділити їх повністю, ви повинні принаймні один з них подвоїти: (double)a/bабо a/(double)bабо (double)a/(double)b.

б) cє double, тому він може прийняти в intзначення на присвоювання: intавтоматично перетворюються doubleі призначено c.

в) Пам’ятайте, що при призначенні вираз праворуч =обчислюється спочатку (згідно з правилом (а) вище, а також без урахування змінної зліва від =), а потім присвоюється змінній зліва від =(відповідно до ( б) вище). Я вважаю, що це завершує картину.


11

За дуже невеликими винятками (я можу придумати лише одне), С ++ визначає весь сенс виразу (або підвиразу) від самого виразу. Що ви робите з результатами виразу, значення не має. У вашому випадку в виразі a / bнемає doubleвидимості; все є int. Тож компілятор використовує ціле ділення. Лише після того, як він отримає результат, він враховує, що з ним робити, і перетворює його в double.


3
Єдиним винятком, про який я можу придумати, є вибір перевантаження функції під час прийому покажчика - значення &funcnameзалежить від того, до якого типу ви його передаєте .
Стів Джессоп

2
@Steve Jessop Це єдиний виняток, про який я також можу придумати. (Але, враховуючи розмір і складність стандарту, я не хотів би поклятися, що я не пропустив жодного.)
Джеймс Канзе,

6

cє doubleзмінною, але присвоєне їй intзначення є значенням, оскільки воно є результатом ділення на два ints, що дає "ціле ділення" (випадання залишку). Отже, те, що відбувається в рядку, c=a/b- це

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

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


6

Якщо розділити два цілих числа, то результат буде цілим числом, незалежно від того, що ви зберігаєте його в подвійному.


5

У мові 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)`

Це один контекст, коли ліва частина призначення / ініціалізації впливає на поведінку правої частини. (Також ініціалізація посилання на масив запобігає розпаду типу масиву, що є ще одним прикладом подібної поведінки.) У всіх інших випадках права частина повністю ігнорує ліву частину.


4

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


2

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

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

У цьому випадку ви отримаєте double var = integer result цілий результат, який переводить цілий результат у подвійне значення після обчислення; в цьому випадку дробові дані вже втрачені. (Більшість мов будуть робити це кастинг, щоб запобігти неточності типів, не викликаючи винятку чи помилки).

Якщо ви хочете зберегти результат як подвійний, ви хочете створити ситуацію, в якій є double var = double result

Найпростіший спосіб зробити це - змусити вираз на правій частині рівняння кинути в подвійне:

c = a/(double)b

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

Після aспливаючого моменту буде звиватися як подвійний, і тепер у вас є поділ між двома парними. Це створить бажаний поділ і призначення.

ПРОТИ, зауважте, що це специфічна мова (і навіть може бути компіляторною), проте майже всі мови (безумовно, всі, які я можу придумати вгорі голови) трактують цей приклад однаково.


Це питання позначено тегом [C ++], і стандарт C ++ диктує, як саме це працює. Не впевнений, що ви маєте на увазі під "мовою", і це, звичайно, не для компілятора, якщо припустимо, що жодні розширення компілятора не задіяні.
Джон Дайблінг

Також неправильно сказати, що "подвійний var = цілий результат, який приводить подвійний var вниз до int". Подвійний не передається до int. Результат int перетворюється в подвійний.
Джон Дайблінг

Я допускав можливість розширень компілятора (я фактично колись мав цю проблему, коли моє середовище "неправильно вводило" результати, і я не могла зрозуміти, чому). А результат - специфічний для мови, оскільки в деяких мовах не дотримуються однакових правил кастингу. Я не вважав, що це специфічний тег C ++. Ви маєте рацію щодо коментаря "подвійний вар = цілий результат". Відредаговано, щоб відобразити це. Дякую!
matthewdunnam

0

Важливим є те, що одним з елементів обчислення є поплавковий-подвійний тип. Потім, щоб отримати подвійний результат, вам потрібно передати цей елемент, як показано нижче:

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

або c = a / static_cast (b);

Або ви можете створити його безпосередньо:

c = 7.0 / 3;

Зауважимо, що один з елементів обчислення повинен мати значення ".0", яке вказує на поділ типу float-double на ціле число. Інакше, незважаючи на те, що змінна c буде подвійною, результат також буде нульовим.


Що відповідає Ваша відповідь, що жоден з інших 9 відповідей ще не є?
болов
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.