Страницы

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

суббота, 30 ноября 2019 г.

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

#cpp #cpp_faq


Я делю два числа друг на друга, например 1 / 2 и ожидаю получить в результате 0.5,
так как присваиваю результат переменной с плавающей точкой, но получаю 0. Вот код:

#include 

int main()
{
    double d = 1 / 2;
    std::cout << d << "\n";    
}



0



В чём может быть проблема?
    


Ответы

Ответ 1



Дело в том, что в 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 в других контекстах так же может давать иные результаты, что были раньше. Поэтому задачу всё же лучше локализовать, и не делать всё поголовно в программе вещественного типа, если это требуется только в некоторых частных случаях.

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

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