Что произойдет, если я постоянно суммирую отрицательные значения в 16-битном регистре

Я застрял в своем проекте. На самом деле я нахожу среднее значение нескольких значений. У меня есть десять значений для среднего. Сумма дает правильный результат в течение некоторого времени, то есть я добавляю отрицательные числа, и результат также является отрицательными числами, но когда значения, среднее значение которых должно быть найдено, имеют большие значения, тогда 16-битный регистр, в котором хранится сумма, дает положительное значение. Что здесь происходит не так.

void SmoothArray()
{ 
    val = 0;
    for(k=0;k<10;k++)
    {
        cc = array[k];
        val += cc; 
    }
    val /= 10; 
}

В этом случае, когда массив имеет много отрицательных местоположений, суммирование является неправильным, что является положительным. Используется язык C. Массив без знака короткий, а val короткий


person Sulaiman Ayub    schedule 10.06.2015    source источник
comment
Прежде всего, укажите типы переменных, которые вы используете.   -  person Eugene Sh.    schedule 10.06.2015
comment
что такое тип массива, cc и val?   -  person Samurai Jack    schedule 10.06.2015


Ответы (4)


Что произойдет, если я постоянно суммирую отрицательные значения в 16-битном регистре

У вас будет неопределенное поведение. Допустим, например, что ваш int является 16-битным, и вы добавляете два значения int INT_MIN и -1, это вызовет неопределенное поведение. В большинстве систем вы получите положительное значение.

Используйте переменную более широкого типа для суммирования ваших значений, например, объявите val типа long, который гарантированно будет иметь ширину не менее 32 бит.

person ouah    schedule 10.06.2015

Сумма, скорее всего, хранится с использованием дополнительного кода 2 (см. https://en.wikipedia.org/wiki/Two%27s_complement). Когда вы достигнете наименьшего отрицательного значения и попытаетесь уменьшить его на единицу, вы получите максимально возможное положительное значение.

Похоже, вам нужно использовать 32-битную переменную для хранения суммированных значений.

person Daniel Frużyński    schedule 10.06.2015

Как уже говорили другие, причиной вашей проблемы является переполнение.

Вот еще один способ усреднения большого количества значений с меньшим риском переполнения, при условии, что у вас есть подсказка о среднем значении. Он основан на следующей формуле:

avg(x) = avg(x - hint) + hint

Например, если вы знаете, что все ваши значения находятся в диапазоне от -200 до -300, вы можете взять hint = -250. Теперь вместо добавления значений от -200 до -300 вы добавляете значения от -50 до +50. Риск переполнения намного меньше, без потери точности.

На самом деле это обычно используется для повышения точности при вычислении среднего большого количества значений с плавающей запятой.

person Serge Ballesta    schedule 10.06.2015

Текущий код OP переполнен.

Конечно, использование более широкого целого числа является самым простым подходом, как ответил @ouah

В противном случае, чтобы предотвратить переполнение, рассмотрите возможность суммирования по частям. Предположим, что переменные имеют тип int, int[]

void SmoothArray(void) { 
    val_msds = 0;
    val_lsd = 0;
    for(k=0;k<10;k++) {
        cc = array[k];
        val_msds += cc/10;  
        val_lsd += cc%10;  
    }
    val = val_msds + val_lsd/10; 
}

Этот подход работает хорошо, пока количество элементов массива (10 в примере OP) меньше sqrt(INT_MAX).

person chux - Reinstate Monica    schedule 10.06.2015