Страницы

Поиск по вопросам

четверг, 11 октября 2018 г.

При делении чисел пропадает дробная часть

Я делю два числа друг на друга, например 1 / 2 и ожидаю получить в результате 0.5, так как присваиваю результат переменной с плавающей точкой, но получаю 0. Вот код:
#include
int main() { double d = 1 / 2; std::cout << d << "
"; }
0
В чём может быть проблема?


Ответ

Дело в том, что в C++, а равно как и в C, C#, Java и многих других языках деление целых чисел даёт исключительно целый же результат, т.е. дробная часть просто отбрасывается.
Стандарт языка буквально так это и говорит [expl.mul]
For integral operands the / operator yields the algebraic quotient with any fractional part discarded;
Подобное поведение обусловлено в первую очередь производительность, т.к. целочисленная арифметика обычно работает быстрее арифметики с плавающей точкой.
То, что результат деления целых чисел присваивается переменной с плавающей точкой не влияет на само деление, т.к. присваивание выполняется уже после факта деления. И мы просто инициализируем вещественную переменную d целым значением 0, которое никак не даст нам чего-то с ненулевой дробной частью, например, ожидаемых 0.5
Чтобы всё-таки добиться желаемого результата нужно производить вещественное деление. Для этого достаточно хотя бы один из операндов привести к вещественному типу. Сделать это можно разными способами. Если используется константа, то достаточно дописать в конце неё точку, например:
double d = 1 / 2.;
или
double d = 1. / 2;
Это выглядит минималистично, но иногда несколько странно, т.к. в математике подобная запись не используется. Для большей наглядности стоит добавить .0, получим:
double d = 1.0 / 2;
Если вместо явного числового литерала используется именованная целочисленная переменная, или функция, возвращающая целый тип, то добавлением точки с нулём проблему уже не решить, и нужно выполнять иное приведение типа, т.е. делать static_cast
int one = 1; int two() { return 2; } double d = static_cast(one) / two();
Но можно использовать и умножение (или деление) любого операнда на 1.0, что так же преобразует результат, который будет выступать операндом последующего деления, к вещественному типу:
double d = (one * 1.0) / two();
Альтернативным вариантом может быть прибавка (или вычитание) вещественного нуля:
double d = (one + 0.0) / two();
Однако, вариант со static_cast будет более правильным, т.к. такие преобразования всегда легче находить в тексте программы, нежели обычные плавающие константы.
Ну и конечно же, если речь идёт о переменных или функциях, то можно просто изменить их тип:
double one = 1; double two() { return 2; } double d = one / two();
В такой ситуации надо не забывать, что использование one и two в других контекстах так же может давать иные результаты, что были раньше. Поэтому задачу всё же лучше локализовать, и не делать всё поголовно в программе вещественного типа, если это требуется только в некоторых частных случаях.

Комментариев нет:

Отправить комментарий