Обнаружил ошибку в своем коде и заметил следующее при вычитании двух чисел, например
unsigned int a, b (при b > a)
длинный результат со знаком = a - b
Результат всегда положительный, даже если b больше. Я ожидал, что, поскольку результат подписан, результат тоже будет преобразован в подписанный, но, видимо, нет. Как я могу это исправить?
@sweenish Многие новички ожидают именно этого. А вот про ОП ничего не могу сказать.
@john Хотя я знаю, это также было описано в моей второй лекции. Не говорю, что ОП — мой ученик, но обычно это одна из первых ошибок, о которой кто-либо узнает.
@sweenish, я знал это конкретно о двойном, но они так и не объяснили нам общее правило, почему это происходит. Все, что я знаю, это то, что если я не приведу удвоение, это будет означать деление без десятичных знаков.
Также обновите, я использовал (подписано) (a-b)... и все работает. То есть это похоже на выполнение (double)(a/b) и получение результата в десятичном виде? для меня не имеет смысла, почему это работает сейчас
Это не работает по причинам, которые Чао описывает в своем комментарии ниже. Похоже, что он работает для большинства значений и не работает в крайних случаях.
Проблема в том, что ваши математические действия выполняются — с unsigned int
s, в результате чего получается unsigned int
— до того, как значение преобразуется в long
. Когда беззнаковый тип преобразуется в более крупный знаковый тип, значение не меняется. Таким образом, ваш результат может быть отрицательным только в том случае, если long
и int
имеют одинаковый размер.
Прежде чем приступить к математическим расчетам, попробуйте преобразовать хотя бы одно из ваших значений в long
. Этого должно быть достаточно, чтобы ваш результат был подписан.
С оговоркой, что присвоение значения, выходящего за пределы диапазона, целому числу со знаком по-прежнему является UB.
ИБ, судя по всему. По крайней мере, до C++20. Но математика все равно может открыть червоточину, если long
и int
имеют одинаковый размер.
Джа. Я неправильно запомнил. Это переполнение, а не присвоение слишком большого значения, то есть и остаётся UB.
Вы уверены, что это правильно? Я добавил (подписанный) (ab), который нашел в Интернете, и теперь он работает. Почему приведение к подписанному типу дает правильный результат, а неявное приведение к подписанному типу — нет?
@dac1n: По сути, потому что (signed)(a-b)
преобразуется в тип, слишком маленький для хранения всех значений unsigned
, поэтому он преобразует беззнаковое в знаковое, как указывает компилятор (особенно распространенным поведением является переинтерпретация как дополнение до двух). Но я предполагаю, что вы хотели long
, потому что, например, (signed)(0-UINT_MAX)
не дает вам той ценности, которую вы ожидаете.
Хорошо, спасибо, это был просто крайний случай. Подводя итог, при выполнении таких операций результатом является «более высокий» тип из двух или тип любого из них, если он одинаковый, независимо от того, какой тип переменной результата вы ей назначаете?
@dac1n: Очень похоже. См. en.cppreference.com/w/cpp/language/usual_arithmetic_conversions (в вашем случае обратите особое внимание на этап 5).
Итак, вы тоже ожидаете, что
double frac = 3 / 5;
даст вам 0,6?