Микроконтролери и електроника
http://mcu-bg.com/mcu_site/

Внимавайте със сметките, ако пишете на C/C++
http://mcu-bg.com/mcu_site/viewtopic.php?f=7&t=700
Страница 1 от 1

Автор:  Реконструктор [ Вто Май 31, 2005 9:46 am ]
Заглавие:  Внимавайте със сметките, ако пишете на C/C++

Става въпрос за случаите, когато имаме аритемтични действия с променливи, по-малки или равни на разредността на компютъра, като резултатът се присвоява на променлива с по-висока разрядност. Да разгледаме следният пример (имаме 8 битов процесор):

Код:
   int8   a = 0xFF;
   int16 b;

   b = a + 1;


Нормално е в този случай да очакваме, че b ще има стойност 256, но на практика b приема стойност 0. Това е така, защото езикът C е контектстно-независим, т.е. не го интересува какво има от лявата страна. Тоест, горния израз е еквивалентен на:

Код:
   int8   a = 0xFF;
   int16 b;

   a = a + 1;
   b = a;


Компилатора просто зарежда a в 8 битов регистър, инкрементира го (при което, естествено, се получава препълване) и го премества в b.
Аналогичен случай е и изразът от типа fload f = 1/3; при който f ще е 0.00, а не както очакваме 0.3(3). За това, трябва да се внимава и действията или да се извършват последователно:

Код:
   int8   a = 0xFF;
   int16 b;

   b = a;
   b = b + 1;


или да се каства навсякъде, където е нужно:

Код:
   int8   a = 0xFF;
   int16 b;

   b = (int16)a + 1;


И да накрая да отбележа, че ако имаме повече променливи от тип int8, които участват в израза, не е необходимо да се кастват всички, а само една от тях:

Код:
   int8   a1, a2, a3;
   int16 b;

   b = (int16)a1 + a2 + a3;


Кастването на целия израз е нищожно, т.е. не води до желания резултат. Т.е. избягвайте изрази, подобни на този:

Код:
   int8   a1, a2, a3;
   int16 b;

   b = (int16)(a1 + a2 + a3);

Автор:  Predator_MF [ Вто Май 31, 2005 10:28 am ]
Заглавие: 

Браво Реконструктор :D За това последното все имам проблеми, все каствам целия израз щото се опасявам че компилатора няма да сложи в сметките a2 и a3 като 16-бита число....

Автор:  bateAz [ Вто Май 31, 2005 1:08 pm ]
Заглавие: 

Добре е да се отбелязват такива най-често срещани грешки. Не че това не го пише във всеки буквар по Цъ, но често се забравя.
Да си кажа и аз. Преди време имаше проблеми с един промишлен пробор, който периодично (на около 1 сек) натрупва някаква величина, например дебит. Примерно - за тази секунда дебитът е бил 10куб. м. за секунда, значи изтеклата вода се е увеличила с 10. Това изглежда така

float total, debit, time;

(периодично)
total += debit * time;

Tова върви добре до време, след което "спира" да натрупва. Направо да ти потънат всички плуващи запетаи. Причина - добавяната величина е по-малка от най-младшия разряд на total. Понеже мутият компилер няма вградени 64-битови float, total се раздели на 2 променливи - младша и старша, по най-изкуствания начин.
Не е лошо подобни ограничения да се имат предвид в сметките.

Автор:  [ Вто Май 31, 2005 1:20 pm ]
Заглавие: 

Модерните компилатори на C/C++ ( Borland,MicroSoft, GNU ) имат опции, които третират този проблем като warning или дори може да се зададе да се генерира като error, когато се опиташ да правиш такива смесвания на типове. Най-добре е при такива преобразувания да се провери в документацията какво прави компилатора.

Автор:  Цецо [ Вто Май 31, 2005 2:27 pm ]
Заглавие: 

c = (int)a +1

тва май решава проблема. Общо взето тия проблеми напрактика са незабелязани от глезени PC програмисти. Там на практика при чистите ПЦ приложения байтови данни не се ползват, всичко се маа на едро - int числа. На кой му пука, памет - колкото щеш. Ама на контролера - там паметта е кът, пестиш, пестиш и кво - стана некоя такава обърквация. И аз там съм затрил сумати време, накрая като видех листинга на асемблер и ми светна.

Автор:  Реконструктор [ Вто Май 31, 2005 2:39 pm ]
Заглавие: 

Цецо написа:
Общо взето тия проблеми напрактика са незабелязани от глезени PC програмисти.


Да, за това я пуснах темата, начинаещите да свикват с особеностите на националния лов. ;) Във фирмуера ми има доста такива места, но по стар навик всичко се извършва последователно, с цел прегледност ма кода - някои ПЦ навици са полезни. Вчера, обаче реших да понатоваря още малко процесора и написах един дългичък израз - доста време ми отне докато разбера ква е работата. :)

Автор:  Реконструктор [ Вто Май 31, 2005 2:40 pm ]
Заглавие: 

bateAz написа:
Понеже мутият компилер няма вградени 64-битови float


Искаш да кажеш, че не поддържа дабъл (double) ??? Ебахти компилатора. :)

Автор:  ДедоБоре [ Вто Май 31, 2005 3:28 pm ]
Заглавие: 

по принцип си прав
явното преобразуване на типове е проява на добър стил.
както и използване на void, където няма параметър.

твоя пример го пуснах на GCC
Код:
unsigned char   a = 0xFF;
unsigned int b;

printf ("a=%d, b=%d\n",a,b);
b = a + 1;
a++;
printf ("a=%d, b=%d\n",a,b);


компилирано с -Wall и без оптимизации, резултата е
a=255, b=0
a=0, b=256

за avr-gcc резултата е същия - b е в 2 регистъра и се прави двубайтово сумиране (без оптимизация е много грозно) и резултата е същия

това, разбира се не обезмисля твоята бележка. на С това му е слилата, че можеш да правиш непозволени присвоявания, което понякога е и голям недостатък. особено като почнеш да хониш някой бъг, дето се случва един път в месеца.

ти какъв компилатор визираш?

Автор:  amon_ra [ Вто Май 31, 2005 3:40 pm ]
Заглавие:  А правилна ли е програмата?

Май използвате компилатори, които не поддържат ANSI стандарта. По принцип целия израз (дори и да съдържа само 8 битови променливи) трябва да се сметне като int.

Конкретно за програмата, тя наистина дава 0, но по друга причина. Спомнете си как се представят отрицателните числа. При 8bit тип данни със знак, 0xff е равно на -1, така че еквивалентната програма е:
Код:
 
int8 a = -1;
int16 b;
b = a + 1;  /* b=-1+1=0*/

Пробвах следната програма със avr-gcc (забележете, че ползвам цели числа без знак):
Код:
uint8_t a = 0xFF;
uint16_t b;
b = a + 1;
PORTA = b>>8;

И това е изхода:
Код:
63:main.c        ****         PORTA = b>>8;
117                            .stabn 68,0,63,.LM4-main
118                    .LM4:
119 000e 81E0                  ldi r24,lo8(1)
120 0010 90E0                  ldi r25,hi8(1)
121 0012 8BBB                  out 59-0x20,r24

Вижда се, че компилатора е сметнал b=0x100, след това е изместил надясно 8 бита и е получил 1, което записва в PORTA.

PS: току що видях, че и ДедоБоре го е тествал на avr-gcc :)

Автор:  Реконструктор [ Вто Май 31, 2005 3:59 pm ]
Заглавие: 

Хех, не знаех за тази опция. :) Значи полето за внимание се разширява - или внимаваш за опцията, или внимаваш с кастовете. :) Но е добре, все пак, че е предвидено такова възможност.

Автор:  Bezmozachen [ Сря Юни 01, 2005 1:09 pm ]
Заглавие: 

Никога не съм писал на нещо различно от asembler за MCU-та и като ви гледам постингите съвсем загубих желание.

Автор:  bateAz [ Сря Юни 01, 2005 1:22 pm ]
Заглавие: 

MoZaKa написа:
Никога не съм писал на нещо различно от asembler за MCU-та и като ви гледам постингите съвсем загубих желание.


Що, бе? C-то е много читава работа и ме кефи. Особено като скорост на писане и дебъгване. Но ЗАДЪЛЖИТЕЛНО си пускам и по един дизасемблерски листинг да го видя тъпия копилатор какви ги е надробил. Е, поне за някои неща :wink:

Автор:  [ Сря Юни 01, 2005 2:19 pm ]
Заглавие: 

Браво на колегата amon_ra, не се сетих да погледна че числата са със знак, иначе и аз се зачудих защо аджиба компилатора ще свива резултата до 8 бита вместо да го разширява до 16-битов резултат.

Автор:  Реконструктор [ Сря Юни 01, 2005 2:21 pm ]
Заглавие: 

pirev написа:
Браво на колегата amon_ra, не се сетих да погледна че числата са със знак, иначе и аз се зачудих защо аджиба компилатора ще свива резултата до 8 бита вместо да го разширява до 16-битов резултат.


Не е тва причината - махни знака и пак ше е същото.

Автор:  Predator_MF [ Сря Юни 01, 2005 5:30 pm ]
Заглавие: 

MoZaKa написа:
Никога не съм писал на нещо различно от asembler за MCU-та и като ви гледам постингите съвсем загубих желание.

Ако някой ме накара да напиша
Код:
         creg1 = (mx2[0]^m_reg2[0]);
         creg2 = (mx2[1]^m_reg2[1]);
         creg3 = (mx1[0]^m_reg1[0]);
         creg4 = (mx1[1]^m_reg1[1]);
         creg5 = mx3^m_reg3;
         if (creg1|creg2|creg3|creg4|creg5)
         { Load_MEMORY();
           Mon = 0;
           MOff = 0;
           for (i=0;i<8;i++)
            { if (bit_test(creg1,i))
                if (bit_test(m_reg2[0],i))
                    bit_set(moff,i);
                else
                    bit_set(mon,i);
            }

това на асемблер, предполагам ще ми отнеме около 20 мин... Със Ц го пишеш за 1 минутка и кашата в процесора си е същата както и на асемблер...

Страница 1 от 1 Часовете са според зоната UTC + 2 часа [ DST ]
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/