Перейти к содержанию
    

Есть вот такой проргамный код для АВР(например):

 

unsigned char a = 100;

float b = 20;

 

далее в программе

 

if( b < -a ){...}

 

так вот оказывается, в таком случаее условие выполняется.

Если кастовать вот так :

 

if( b < - (int) a ) {...}

 

тогда все ок. Объясните почему? Я не могу понять, что с чем сравнивается, если не кастовать, и почему так происходит?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 Gogan - последний вариант - как бы правильней - вы сводите всё как бы 1 типу а потом уже идёт выравнивание - так надо делать... Это ж си а не ассемблер - где вы сами задаёте математику..

А на счёт того что с чем сравнивается - советую залянуть в ассемблерный код - его в АВРстудии легко получить - там всё видно будет...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Есть вот такой проргамный код для АВР(например):

 

unsigned char a = 100;

float b = 20;

 

далее в программе

 

if( b < -a ){...}

 

так вот оказывается, в таком случаее условие выполняется.

Если кастовать вот так :

 

if( b < - (int) a ) {...}

 

тогда все ок. Объясните почему? Я не могу понять, что с чем сравнивается, если не кастовать, и почему так происходит?

так происходит потому, что тип переменной а unsigned. отрицание вовсе не меняет этот тип, а просто устанавливает старший бит (получается 100 + 128 = 228), после чего производится беззнаковое сравенине. если Вы будете приводить а к типу unsigned int, будет то же самое

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

так происходит потому, что тип переменной а unsigned. отрицание вовсе не меняет этот тип, а просто устанавливает старший бит (получается 100 + 128 = 228), после чего производится беззнаковое сравенине. если Вы будете приводить а к типу unsigned int, будет то же самое

Логика почти правильная, за исключением "просто устанавливает старший бит". На самом деле делается честное преобразование 100 0x64 в (-100) 0x9C, но т.к. это unsigned char, то 0x9C трактуется как положительное число 156. 20 < 156 ? - да.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Огромное спасибо за ответы! Теперь уяснил. Сегодня писал программу, так вот дебагил одни ошибки из-за преобразований типов, потратил много времени.

 

Если кому еще интересна эта тема, вот такое еще было:

 

eeprom unsigned char edata[20][8]={{90,0,...},{}...};

unsigned int a;

 

далее в тексте

a=edata[0][0]+edata[0][1]<<8;

 

т.е. интегер хранится в двух первых байтах, сначала младший потом старший. Компилер выдал, что edata[0][1]<<8 получится в любом случае 0. Я переписал

a=(int)edata[0][0]+edata[0][1]<<8;

 

опять неправильно. Компилер не ругался, но результат был 90х256=23040. Правильным вариантом оказалось:

a=edata[0][0]+(int)edata[0][1]<<8;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

unsigned char a = 100;

float b = 20;

 

далее в программе

 

if( b < -a ){...}

 

так вот оказывается, в таком случаее условие выполняется.

Если кастовать вот так :

 

if( b < - (int) a ) {...}

 

тогда все ок. Объясните почему? Я не могу понять, что с чем сравнивается, если не кастовать, и почему так происходит?

На мой взгляд, компилятор не прав. Согласно стандарту С он должен оба эти if-а обрабаотывать одинаково и так, как второй. Есть такая штука, как integer promotion rules (integer promotions), согласно им любой целочисленный тип, который меньше int-а, должен приводиться к int перед вычислением выражений. Даже для

char a,b,c;
a = b + c;

- привести к int, сложить, результат усечь и присвоить a.

Это требования к "абстрактной машине", дальше можно дать свободу и не делать приведение к int, но только в случае, если от такого неделания результат не изменится.

В данном случае -a это уже подвыражение с унарным минусом, перед его вычислением компилятору надо было привести a к int (что во втором примере сделано вручную), потом применить минус, потом, согласно правилам автоматических преобразований типов, результат привести к float и сравнить.

Так что тут компилятор не имел права делать -a в пределах байта.

 

eeprom unsigned char edata[20][8]={{90,0,...},{}...};

unsigned int a;

 

далее в тексте

a=edata[0][0]+edata[0][1]<<8;

 

т.е. интегер хранится в двух первых байтах, сначала младший потом старший. Компилер выдал, что edata[0][1]<<8 получится в любом случае 0. Я переписал

a=(int)edata[0][0]+edata[0][1]<<8;

 

опять неправильно. Компилер не ругался, но результат был 90х256=23040. Правильным вариантом оказалось:

a=edata[0][0]+(int)edata[0][1]<<8;

Выбрось каку!!!

Правильный компилятор (соответствующий стандарту) во всех трёх приведённых тобой вариантах должен был дать одинаковый результат, впрочем, не тот, который ты ожидаешь :)

Приведение к int делать вообще не нужно, оно должно было быть сделано согласно integer promotions самим компилятором.

А вот дальше - у оператора сложения приоритет выше, чем у операции сдвига (с моей точки зрения, приоритет сдвигов должен бы быть между приоритетами *% и +-, но K&R когда-то решили иначе и стандарт есть стандарт), поэтому

a + b << 8;

должно вычисляться всегда как

( a + b ) << 8;

 

А то, что хотелось сделать, следует записывать как

a + ( b << 8 );

 

 

p.s. Не понимаю, как можно пользоваться компилятором, настолько не соответствующим стандарту...

Или он позиционируется как "компилятор С-подобного языка программирования" ???

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А то, что хотелось сделать, следует записывать как

a + ( b << 8 );

Ухты, был уверен что приоритеты по другому стоят. Спасибо за разъяснение!

 

p.s. Не понимаю, как можно пользоваться компилятором, настолько не соответствующим стандарту...

Или он позиционируется как "компилятор С-подобного языка программирования" ???

 

Вот из хелпа:

It is important to note that if the Project|Configure|C Compiler|Code Generation|Promote char to int option isn't checked or the #pragma promotechar+ isn't used, the char, respectively unsigned char, type operands are not automatically promoted to int , respectively unsigned int, as in compilers targeted for 16 or 32 bit CPUs.

 

This helps writing more size and speed efficient code for an 8 bit CPU like the AVR.

 

To prevent overflow on 8 bit addition or multiplication, casting may be required.

 

The compiler issues warnings in these situations.

Т.е. опцию привидения к int можно включить (это я уже потом понял). Я пишу я для 8-битного авр...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Выбрось каку!!!

Обьясните тупому,плиз.

eeprom unsigned char edata[20][8]={{90,0,...},{}...};

unsigned int a;

Запись a=edata[0][0]+edata[0][1]<<8; эквивалентна a= (edata[0][0]+edata[0][1] ) << 8;

Т.е вначале складываются 2 unsigned char и с какого перепуга компилятор должен их автоматически приводить к int?А вот сдвиг результата влево да,уже дожен.Тока ошибка уже гарантирована.

Имхо,есичё. :05:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Имхо правильный вариант:

eeprom unsigned int edata[20][4]={{1578,0,...},{}...};

unsigned int a;

a=edata[0][0];

:)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Логика почти правильная, за исключением "просто устанавливает старший бит". На самом деле делается честное преобразование 100 0x64 в (-100) 0x9C, но т.к. это unsigned char, то 0x9C трактуется как положительное число 156. 20 < 156 ? - да.

согласен, вчера был тяжелый день :)

 

Выбрось каку!!!

Обьясните тупому,плиз.

eeprom unsigned char edata[20][8]={{90,0,...},{}...};

unsigned int a;

Запись a=edata[0][0]+edata[0][1]<<8; эквивалентна a= (edata[0][0]+edata[0][1] ) << 8;

Т.е вначале складываются 2 unsigned char и с какого перепуга компилятор должен их автоматически приводить к int?А вот сдвиг результата влево да,уже дожен.Тока ошибка уже гарантирована.

Имхо,есичё. :05:

согласно стандарту integer promotion эта операция должна производиться всегда (во избежание overflow), а потом уже результат усекаться до размера целевой переменной. представьте себе

unsigned char c1 = 250, c2 = 250;
int a = c1 + c2;

если не выполнить promotion ДО вычисления суммы, ошибка, как Вы говорите, гарантирована

Изменено пользователем sergik_vrn

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Дык я про это и пишу.В вашем примере это несомненно,а у аффтара составное выражение,и в этом

случае и автоматическое приведение будет только для операции сдвига.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

[..]

а у аффтара составное выражение,и в этом

случае и автоматическое приведение будет только для операции сдвига.

почему Вы так думаете? вообще-то, приведение должно производиться до любых вычислений, в этом его смысел. то, что автор перепутал приоритет операций, на это не влияет. то есть, последовательность в его случае такая: promote <val1> - promote <val2> - add - shift - truncate - assign

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

То есть если у меня сложная составная операция с данными разных типов и результат имеет еще какой-

нибудь другой тип,то вначале все типы данных приводятся к типу результата и тока потом выполняется

вся арифметика?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Приведение к int делать вообще не нужно, оно должно было быть сделано согласно integer promotions самим компилятором.

ISO9899 п.6.3.1.1 абз.2

The following may be used in an expression wherever an int or unsigned int may be used:

— An object or expression with an integer type whose integer conversion rank is less than the rank of int and unsigned int.

— A bit-field of type _Bool, int, signed int, or unsigned int.

If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.

Так, должно или может быть сделано?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

То есть если у меня сложная составная операция с данными разных типов и результат имеет еще какой-

нибудь другой тип,то вначале все типы данных приводятся к типу результата и тока потом выполняется

вся арифметика?

не к типу результата, а к int. а если в выражении есть float, то к float. а потом результат усекается до типа результата. это если по стандарту

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...