Почему я получаю ошибки при использовании целых без знака в выражении с C ++? - PullRequest
2 голосов
/ 17 мая 2010

Учитывая следующий фрагмент (псевдо-C ++) кода:

float x=100, a=0.1;
unsigned int height = 63, width = 63;
unsigned int hw=31;
for (int row=0; row < height; ++row)
{
    for (int col=0; col < width; ++col)
    {
        float foo = x + col - hw + a * (col - hw);
        cout << foo << " ";
    }
    cout << endl;
}

Значения foo облажаются для половины массива в местах, где (col - hw) отрицательно. Я понял, потому что col это int и идет первым, что эта часть выражения преобразуется в int и становится отрицательной. К сожалению, по-видимому, это не так, я получаю переполнение без знака значения, и я не знаю, почему.

Как мне решить эту проблему? Использовать приведение для всего или части выражения? Какой тип приведений (C-style или static_cast <...>)? Есть ли накладные расходы на использование приведений (мне нужно, чтобы это работало быстро!)?

РЕДАКТИРОВАТЬ: я изменил все свои неподписанные целые на обычные, но я все еще задаюсь вопросом, почему я получил это переполнение в этой ситуации.

Ответы [ 5 ]

7 голосов
/ 17 мая 2010

Целые числа без знака реализуют арифметику без знака. Арифметика без знака является арифметикой по модулю. Все значения корректируются по модулю 2 ^ N, где N - количество битов в представлении значения типа без знака.

Проще говоря, арифметика без знака всегда дает неотрицательные значения. Каждый раз, когда выражение должно приводить к отрицательному значению, значение фактически «оборачивается» 2 ^ N и становится положительным.

Когда вы смешиваете целое со знаком и без знака в выражении [sub-], арифметика без знака «выигрывает», то есть вычисления выполняются в области без знака. Например, когда вы делаете col - hw, это интерпретируется как (unsigned) col - hw. Это означает, что для col == 0 и hs == 31 вы не получите -31 в результате. Вместо этого вы получите UINT_MAX - 31 + 1, что обычно является огромным положительным значением.

Сказав это, я должен отметить, что, по моему мнению, всегда полезно использовать неподписанные типы для представления неотрицательных значений. На самом деле, на практике большинство (или, по крайней мере, половина) целочисленных переменных в C / C ++ должны иметь типы без знака. Ваша попытка использовать неподписанные типы в вашем примере вполне оправдана (если вы правильно поняли цель). Более того, я бы использовал unsigned для col и row. Тем не менее, вы должны помнить, как работает беззнаковая арифметика (как описано выше) и писать свои выражения соответственно. Большую часть времени выражение можно переписать так, чтобы оно не пересекало границы беззнакового диапазона, т. Е. В большинстве случаев нет необходимости явно приводить что-либо к типу со знаком. В противном случае, если вам в конечном итоге потребуется работать с отрицательными значениями, правильное приведение к типу со знаком должно решить проблему.

2 голосов
/ 17 мая 2010

Как насчет создания height, width и hw подписанных целых?Что вы на самом деле получаете, делая их без подписи?Смешивание целых чисел со знаком и без знака всегда вызывает проблемы.На первый взгляд, по крайней мере, не похоже, что вы что-то получаете, используя здесь значения без знака.Таким образом, вы могли бы также сделать их все подписанными и избавить себя от хлопот.

1 голос
/ 17 мая 2010

У вас есть правила преобразования в обратном направлении - когда вы смешиваете подписанную и неподписанную версии одного и того же типа, подписанный операнд преобразуется в неподписанное.

0 голосов
/ 17 мая 2010

Приведение не будет происходить автоматически - арифметика без учета еще используется. Обычный пример - int / int = int, даже если данные теряются из-за того, что они не конвертируются в float. Я бы использовал подписанный int, если это невозможно сделать из-за слишком малого INT_MAX.

0 голосов
/ 17 мая 2010

Если вы хотите, чтобы это было быстро, вы должны static_cast для всех беззнаковых значений, прежде чем начинать цикл, и использовать их версии int вместо unsigned int. Вы по-прежнему можете требовать, чтобы входные данные были без знака, а затем просто приводить их по пути к вашему алгоритму, чтобы сохранить необходимый домен для вашей функции.

...