Я кодирую физическое моделирование, и недавно я столкнулся с аномальными результатами. Мне удалось отладить мою программу, ошибка была в делении большого двойного на большое целое, что-то вроде:
cout << my_large_double/my_large_int << endl
с my_large_double порядка -10 ^ {9} и произведение моих двух целых чисел порядка 10 ^ {9} возвращало что-то положительное порядка 1.
Я исправил это путем наложения преобразования в удвоение в знаменателе:
cout << my_large_double/( (double)my_large_int1*my_large_int2) << endl
Но я хотел бы понять, откуда исходит ошибка, и есть ли способы предотвратить ее, как правило?
Обновление: я пропустил деталь, которая имеет значение в моем первом вопросе: int на самом деле является продуктом двух целых.
Это зависит от того, как именно было написано выражение.
Если вы напишите это:
my_large_double / my_large_int1 / my_large_int2
тогда это эквивалентно:
(my_large_double / my_large_int1) / my_large_int2
который должен дать вам достаточно точные результаты; my_large_int1
повышен до double
до первого деления, и my_large_int2
повышен до double
до второго дивизиона.
Если вы напишите это:
my_large_double / (my_large_int1 * my_large_int2)
затем умножение выполняется в виде двух целочисленных переменных, и в зависимости от их значений у вас может быть переполнение (которое может дать вам гораздо меньшее значение, чем математический продукт — хотя, строго говоря, поведение целочисленного переполнения со знаком не определено ).
Важно помнить, что в большинстве случаев каждое выражение C эффективно оценивается изолированно; его тип не зависит от контекста, в котором он появляется. Выражение my_large_int1 * my_large_int2
является целочисленным умножением, даже если результат является операндом деления с плавающей запятой или назначен переменной с плавающей запятой.
Любая операция, операнды которой являются целыми числами, является целочисленной операцией. Если один операнд double
а другой int
, int
операнд повышен до double
,
Даже это:
double temp = my_large_int1 * my_large_int2;
... my_large_double / temp ...
выполнит целочисленное умножение перед использованием результата для инициализации temp
, и это:
my_large_double / (double)(my_large_int1 * my_large_int2)
имеет ту же проблему.
Как вы обнаружили, решение состоит в том, чтобы привести один или оба целочисленных операнда к double
:
my_large_double / ((double)my_large_int1 * (double)my_large_int2)
(Вы могли бы также использовать их обоих, только для симметрии и ясности.)
Формат IEEE double
может держать int
без потери точности до 53 бит по сравнению с 31 битом, типичным для int
, Ваше решение хорошее, конвертировать в double
заранее, чтобы вы не столкнулись с целочисленным переполнением при умножении.