Отговори на тема  [ 15 мнения ] 
Внимавайте със сметките, ако пишете на C/C++ 
Автор Съобщение
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Съб Сеп 25, 2004 12:32 pm
Мнения: 8382
Местоположение: София
Мнение Внимавайте със сметките, ако пишете на 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);


Вто Май 31, 2005 9:46 am
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Чет Окт 07, 2004 1:22 pm
Мнения: 1949
Местоположение: София
Мнение 
Браво Реконструктор :D За това последното все имам проблеми, все каствам целия израз щото се опасявам че компилатора няма да сложи в сметките a2 и a3 като 16-бита число....


Вто Май 31, 2005 10:28 am
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Нед Сеп 26, 2004 4:11 pm
Мнения: 3750
Местоположение: София
Мнение 
Добре е да се отбелязват такива най-често срещани грешки. Не че това не го пише във всеки буквар по Цъ, но често се забравя.
Да си кажа и аз. Преди време имаше проблеми с един промишлен пробор, който периодично (на около 1 сек) натрупва някаква величина, например дебит. Примерно - за тази секунда дебитът е бил 10куб. м. за секунда, значи изтеклата вода се е увеличила с 10. Това изглежда така

float total, debit, time;

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

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


Вто Май 31, 2005 1:08 pm
Профил ICQ
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Пет Фев 25, 2005 1:58 pm
Мнения: 4585
Местоположение: US
Мнение 
Модерните компилатори на C/C++ ( Borland,MicroSoft, GNU ) имат опции, които третират този проблем като warning или дори може да се зададе да се генерира като error, когато се опиташ да правиш такива смесвания на типове. Най-добре е при такива преобразувания да се провери в документацията какво прави компилатора.


Вто Май 31, 2005 1:20 pm
Профил WWW
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Пон Сеп 27, 2004 9:22 am
Мнения: 15501
Местоположение: София
Мнение 
c = (int)a +1

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

_________________
"Да еба и шибаната държава" мислеше си Гошо, докато се опитваше да улучи кофата за боклук от балкона на осмия етаж.


Вто Май 31, 2005 2:27 pm
Профил ICQ
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Съб Сеп 25, 2004 12:32 pm
Мнения: 8382
Местоположение: София
Мнение 
Цецо написа:
Общо взето тия проблеми напрактика са незабелязани от глезени PC програмисти.


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


Вто Май 31, 2005 2:39 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Съб Сеп 25, 2004 12:32 pm
Мнения: 8382
Местоположение: София
Мнение 
bateAz написа:
Понеже мутият компилер няма вградени 64-битови float


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


Вто Май 31, 2005 2:40 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Нед Ное 21, 2004 11:31 pm
Мнения: 10088
Мнение 
по принцип си прав
явното преобразуване на типове е проява на добър стил.
както и използване на 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 регистъра и се прави двубайтово сумиране (без оптимизация е много грозно) и резултата е същия

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

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


Вто Май 31, 2005 3:28 pm
Профил
Ранг: Минаващ
Ранг: Минаващ
Аватар

Регистриран на: Вто Дек 28, 2004 5:46 pm
Мнения: 5
Мнение А правилна ли е програмата?
Май използвате компилатори, които не поддържат 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:40 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Съб Сеп 25, 2004 12:32 pm
Мнения: 8382
Местоположение: София
Мнение 
Хех, не знаех за тази опция. :) Значи полето за внимание се разширява - или внимаваш за опцията, или внимаваш с кастовете. :) Но е добре, все пак, че е предвидено такова възможност.


Вто Май 31, 2005 3:59 pm
Профил
Ранг: Популярен
Ранг: Популярен
Аватар

Регистриран на: Сря Апр 27, 2005 2:47 pm
Мнения: 349
Местоположение: Varna
Мнение 
Никога не съм писал на нещо различно от asembler за MCU-та и като ви гледам постингите съвсем загубих желание.


Сря Юни 01, 2005 1:09 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Нед Сеп 26, 2004 4:11 pm
Мнения: 3750
Местоположение: София
Мнение 
MoZaKa написа:
Никога не съм писал на нещо различно от asembler за MCU-та и като ви гледам постингите съвсем загубих желание.


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


Сря Юни 01, 2005 1:22 pm
Профил ICQ
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Пет Фев 25, 2005 1:58 pm
Мнения: 4585
Местоположение: US
Мнение 
Браво на колегата amon_ra, не се сетих да погледна че числата са със знак, иначе и аз се зачудих защо аджиба компилатора ще свива резултата до 8 бита вместо да го разширява до 16-битов резултат.


Сря Юни 01, 2005 2:19 pm
Профил WWW
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Съб Сеп 25, 2004 12:32 pm
Мнения: 8382
Местоположение: София
Мнение 
pirev написа:
Браво на колегата amon_ra, не се сетих да погледна че числата са със знак, иначе и аз се зачудих защо аджиба компилатора ще свива резултата до 8 бита вместо да го разширява до 16-битов резултат.


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


Сря Юни 01, 2005 2:21 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Чет Окт 07, 2004 1:22 pm
Мнения: 1949
Местоположение: София
Мнение 
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 минутка и кашата в процесора си е същата както и на асемблер...


Сря Юни 01, 2005 5:30 pm
Профил
Покажи мненията от миналия:  Сортирай по  
Отговори на тема   [ 15 мнения ] 

Кой е на линия

Потребители разглеждащи този форум: jordan-bg и 8 госта


Вие не можете да пускате нови теми
Вие не можете да отговаряте на теми
Вие не можете да променяте собственото си мнение
Вие не можете да изтривате собствените си мнения
Вие не можете да прикачвате файл

Търсене:
Иди на:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.
Хостинг и Домейни