2013-04-22 6 views
1

У меня проблема с арифметической операцией с целыми переменными без знака.Неверный результат при работе с целыми целыми знаками

Все переменные определяются как uint32_t. Это арифметическая операция:

batt += (uint32_t) ((((charg - discharg) * (time_now - time_old))/1000) + 0.5); 

Значения до операции являются:

batt = 8999824 
charg = 21 
discharg = 1500 
time_now = 181 
time_old = 132 

Проблема заключается в том, что результат после операции

batt = 13294718 

вместо

batt = 8999752 

В чем причина?

Заранее спасибо.

+0

'charge - discharge' make unsigned int overflow. – MYMNeo

+0

«Зарядка - разрядка» не соответствует вашим входам. –

+0

«зарядка - разряд» ниже нуля и, таким образом, переполняется. Это намеренно? – CodesInChaos

ответ

1

У вас есть 2 проблемы.

  1. charg < discharg как это создает наматывается вокруг ответ 4294965817 для charg - discharg. Ниже вы узнаете, почему вы закончили с 13294718.

  2. Сделайте смещение (+ 0,5) до /1000, иначе целочисленное деление будет готово, чтобы отбросить дробную часть.

Рекомендуемое исправление 1: страховое поручение> = разряд.

ИЛИ

Рекомендуемое исправление 1: изменение CHARG, discharg, time_now, time_old и, возможно, фетра к int32_t.

Рекомендуется исправить 2: изменить округление к batt += (uint32_t) ((Product/1000.0) + 0.5);

ИЛИ

Рекомендуется исправить 2: изменить округление к batt += (Product + 500*sign(Product))/1000;


кажущейся странствующих код - шаг за шагом.

uint32_t batt = 8999824; 
uint32_t charg = 21; 
uint32_t discharg = 1500; 
uint32_t time_now = 181; 
uint32_t time_old = 132; 
// batt += (uint32_t) ((((charg - discharg) * (time_now - time_old))/1000) + 0.5); 

// The big problem occurs right away. 
// Since charg is less than discharg, and unsigned arithmetic "wrap around", 
// you get (21 - 1500) + 2**32 = (21 - 1500) + 4294967296 = 4294965817 
uint32_t d1 = charg - discharg; 
uint32_t d2 = time_now - time_old; // 49 
// The product of d1 and d2 will overflow and the result is mod 4294967296 
// (49 * 4294965817) = 210453325033 
// 210453325033 mod 4294967296 = 4294894825 
uint32_t p1 = d1 * d2; 
uint32_t q1 = p1/1000; // 4294894825/1000 = 4294894.825. round to 0 --> 4294894 
double s1 = q1 + 0.5; // 4294894 + 0.5 --> 4294894.5; 
uint32_t u1 = (uint32_t) s1; // 4294894.5 round to 0 --> 4294894 
batt += u1; // 8999824 + 4294894 --> 13294718 
1

Результат charg - discharg отрицательный, при этом все выражение является отрицательным, то есть довольно большим unsigned.

+0

Разница маловероятна, поскольку оба значения имеют одинаковый неподписанный тип (он может быть отрицательным, если этот тип меньше, чем 'int'). Таким образом, разница потенциально может иметь большое положительное значение. Другие возможные проблемы включают переполнение в умножении и потери точности в делении. –