|
Type conversions в CodeVision, unsigned char->float |
|
|
|
Dec 28 2007, 14:33
|

Частый гость
 
Группа: Свой
Сообщений: 152
Регистрация: 11-10-05
Из: Воронеж
Пользователь №: 9 491

|
Цитата(Gogan @ Dec 28 2007, 16:21)  Есть вот такой проргамный код для АВР(например):
unsigned char a = 100; float b = 20;
далее в программе
if( b < -a ){...}
так вот оказывается, в таком случаее условие выполняется. Если кастовать вот так :
if( b < - (int) a ) {...}
тогда все ок. Объясните почему? Я не могу понять, что с чем сравнивается, если не кастовать, и почему так происходит? так происходит потому, что тип переменной а unsigned. отрицание вовсе не меняет этот тип, а просто устанавливает старший бит (получается 100 + 128 = 228), после чего производится беззнаковое сравенине. если Вы будете приводить а к типу unsigned int, будет то же самое
|
|
|
|
|
Dec 28 2007, 18:33
|

Частый гость
 
Группа: Участник
Сообщений: 83
Регистрация: 25-10-07
Из: Киев
Пользователь №: 31 728

|
Огромное спасибо за ответы! Теперь уяснил. Сегодня писал программу, так вот дебагил одни ошибки из-за преобразований типов, потратил много времени.
Если кому еще интересна эта тема, вот такое еще было:
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;
|
|
|
|
|
Dec 28 2007, 20:57
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(Gogan @ Dec 28 2007, 15:21)  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 в пределах байта. Цитата(Gogan @ Dec 28 2007, 20:33)  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. Не понимаю, как можно пользоваться компилятором, настолько не соответствующим стандарту... Или он позиционируется как "компилятор С- подобного языка программирования" ???
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Dec 29 2007, 06:25
|

Частый гость
 
Группа: Участник
Сообщений: 83
Регистрация: 25-10-07
Из: Киев
Пользователь №: 31 728

|
Цитата(ReAl @ Dec 28 2007, 22:57)  А то, что хотелось сделать, следует записывать как a + ( b << 8 ); Ухты, был уверен что приоритеты по другому стоят. Спасибо за разъяснение! Цитата(ReAl @ Dec 28 2007, 22:57)  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-битного авр...
|
|
|
|
|
Dec 29 2007, 06:25
|

Знающий
   
Группа: Свой
Сообщений: 902
Регистрация: 2-01-06
Из: Краснодар
Пользователь №: 12 768

|
Выбрось каку!!! Обьясните тупому,плиз. 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?А вот сдвиг результата влево да,уже дожен.Тока ошибка уже гарантирована. Имхо,есичё.
--------------------
"Hello, word!" - 17 errors 56 warnings
|
|
|
|
|
Dec 29 2007, 06:47
|
Местный
  
Группа: Свой
Сообщений: 408
Регистрация: 21-10-06
Из: Санкт-Петербург
Пользователь №: 21 527

|
Имхо правильный вариант: eeprom unsigned int edata[20][4]={{1578,0,...},{}...}; unsigned int a; a=edata[0][0];
|
|
|
|
|
Dec 29 2007, 06:54
|

Частый гость
 
Группа: Свой
Сообщений: 152
Регистрация: 11-10-05
Из: Воронеж
Пользователь №: 9 491

|
Цитата(defunct @ Dec 28 2007, 18:15)  Логика почти правильная, за исключением "просто устанавливает старший бит". На самом деле делается честное преобразование 100 0x64 в (-100) 0x9C, но т.к. это unsigned char, то 0x9C трактуется как положительное число 156. 20 < 156 ? - да. согласен, вчера был тяжелый день  Цитата(WHALE @ Dec 29 2007, 09:25)  Выбрось каку!!! Обьясните тупому,плиз. 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?А вот сдвиг результата влево да,уже дожен.Тока ошибка уже гарантирована. Имхо,есичё.  согласно стандарту integer promotion эта операция должна производиться всегда (во избежание overflow), а потом уже результат усекаться до размера целевой переменной. представьте себе Код unsigned char c1 = 250, c2 = 250; int a = c1 + c2; если не выполнить promotion ДО вычисления суммы, ошибка, как Вы говорите, гарантирована
Сообщение отредактировал sergik_vrn - Dec 29 2007, 06:57
|
|
|
|
|
Dec 29 2007, 08:21
|

Частый гость
 
Группа: Свой
Сообщений: 152
Регистрация: 11-10-05
Из: Воронеж
Пользователь №: 9 491

|
Цитата(WHALE @ Dec 29 2007, 10:15)  [..] а у аффтара составное выражение,и в этом случае и автоматическое приведение будет только для операции сдвига. почему Вы так думаете? вообще-то, приведение должно производиться до любых вычислений, в этом его смысел. то, что автор перепутал приоритет операций, на это не влияет. то есть, последовательность в его случае такая: promote <val1> - promote <val2> - add - shift - truncate - assign
|
|
|
|
|
Dec 29 2007, 08:38
|

Гуру
     
Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954

|
Цитата(ReAl @ Dec 28 2007, 23:57)  Приведение к 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. Так, должно или может быть сделано?
|
|
|
|
|
Dec 29 2007, 08:39
|

Частый гость
 
Группа: Свой
Сообщений: 152
Регистрация: 11-10-05
Из: Воронеж
Пользователь №: 9 491

|
Цитата(WHALE @ Dec 29 2007, 11:29)  То есть если у меня сложная составная операция с данными разных типов и результат имеет еще какой- нибудь другой тип,то вначале все типы данных приводятся к типу результата и тока потом выполняется вся арифметика? не к типу результата, а к int. а если в выражении есть float, то к float. а потом результат усекается до типа результата. это если по стандарту
|
|
|
|
|
Dec 29 2007, 09:15
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(Палыч @ Dec 29 2007, 10:38)  ISO9899 п.6.3.1.1 абз.2 Так, должно или может быть сделано? Могут перечисленные типы применяться в тех местах, где может применяться int или unsigned int Приведение должно быть сделано к int, если он может вместить все значения, иначе - к unsigned int. Чтобы долго не спорить, тот же документ параграф 5.1.2.3 Program execution, абзац 10, пример 2 (выделения мои) Цитата 10 EXAMPLE 2 In executing the fragment char c1, c2; /* ... */ c1 = c1 + c2; the ‘‘integer promotions’’ require that the abstract machine promote the value of each variable to int size and then add the two ints and truncate the sum. Provided the addition of two chars can be done without overflow, or with overflow wrapping silently to produce the correct result, the actual execution need only produce the same result, possibly omitting the promotions. Т.е. promotion выбросить можно только тогда, когда результат останется корректным. Так что "можно не делать только в том случае, если для вариантов 'сделать' и 'не сделать' результат совпадает". В случае ( a + b ) << 8 если его не сделать, результат некорректен. Gogan, Дополнительная оптимизация "на шару" за счёт отступления от стандарта это может и хорошо, но только не удивляйтесь потом, если чужие рабочие исходники не будут работать у Вас или если Вы позже перейдёте на другой контроллер или компилятор и будете долго искать у себя ошибки из-за привычек, полученных в процессе работы с несоответствующим стандарту инструментом. А я лучше смирюсь с "хвостами" недооптимизированных приведений (есть такая бяка у avr-gcc), чем буду думать, почему что-то работает не так, как я ожидаю. Цитата(WHALE @ Dec 29 2007, 10:29)  То есть если у меня сложная составная операция с данными разных типов и результат имеет еще какой- нибудь другой тип,то вначале все типы данных приводятся к типу результата и тока потом выполняется вся арифметика? Люди, ну найдите да почитайте стандрат. Да, нудновато, но зато прояснится что-то в голове. CODE 6.3.1.8 Usual arithmetic conversions 1 Many operators that expect operands of arithmetic type cause conversions and yield result types in a similar way. The purpose is to determine a common real type for the operands and result. For the specified operands, each operand is converted, without change of type domain, to a type whose corresponding real type is the common real type. Unless explicitly stated otherwise, the common real type is also the corresponding real type of the result, whose type domain is the type domain of the operands if they are the same, and complex otherwise. This pattern is called the usual arithmetic conversions:
First, if the corresponding real type of either operand is long double, the other operand is converted, without change of type domain, to a type whose corresponding real type is long double.
Otherwise, if the corresponding real type of either operand is double, the other operand is converted, without change of type domain, to a type whose corresponding real type is double.
Otherwise, if the corresponding real type of either operand is float, the other operand is converted, without change of type domain, to a type whose corresponding real type is float.51)
Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:
If both operands have the same type, then no further conversion is needed. Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.
Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.
Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type. Приводится всё к самому "ёмкому" по диапазону значений типу, если он выше или равен int. Всё, что рангом ниже int - поднимается до int даже если самого int в выражении нет.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|