Приведение типов в выражениях C/C++ (комментарии)
Это сообщение сгенерировано автоматически.
Интересно. Нелогично сделано в x86 есть sub al,bl итд, можно работать с разрядностью 8,16,32,64
Когда есть онлайн компиляторы, то уже следует прикладывать тесты:
http://liveworkspace.org/code/de5b8f4d940fe5d16f29da20b8ec5b07
http://liveworkspace.org/code/6af8c65278eabf14baaf1f7911b79399
>Порой кажется, что приведение типа явно лишнее, но на самом деле оно необходимо:
>unsigned short time = 5;
>unsigned short last_time = 65531;
>float delta_time2 = (float) (unsigned short) (time - last_time);
>// в скобках int (-65526), далее приведение к unsigned short (10), результат равен 10.0f
Вы так говорите, как будто 5-65531 равно 10.
laMer007
> Вы так говорите, как будто 5-65531 равно 10.
Для unsigned типов в нормальных языках оно равно Integer Overflow
Абрам Софтрендер
> Для unsigned типов в нормальных языках оно равно Integer Overflow
В MS VS 2008 в настройках компиляции есть ключ, отвечающий за обнаружение таких Integer Overflow в debug.
Я провёл тщательный тест, над которым можно задуматься. http://ideone.com/QMIil
#include <stdio.h> #include <stdint.h> #include <time.h> int main () { unsigned short c1 = 100500; // warning unsigned short c2 = ( int)( ( int16_t)5 - ( int16_t)7); // no warning printf( "%d\n", ( int16_t)5 - ( int16_t)7); // -2 printf( "%u\n", ( int16_t)5 - ( int16_t)7); // 4294967294 printf( "%d\n", ( int16_t)65534); // -2 puts( ""); printf( "%d\n", ( uint16_t)5 - ( uint16_t)7); // -2 printf( "%u\n", ( uint16_t)5 - ( uint16_t)7); // 4294967294 printf( "%d\n", ( uint16_t)65534); // 65534 puts( ""); printf( "%hd\n", ( int16_t)40000 + ( int16_t)40000); // 14464 printf( "%d\n", ( int16_t)40000 + ( int16_t)40000); // -51072 printf( "%u\n", ( int16_t)40000 + ( int16_t)40000); // 4294916224 puts( ""); printf( "%hd\n", ( uint16_t)40000 + ( uint16_t)40000); // 14464 printf( "%d\n", ( uint16_t)40000 + ( uint16_t)40000); // 80000 printf( "%u\n", ( uint16_t)40000 + ( uint16_t)40000); // 80000 puts( ""); printf( "%hd\n", ( int16_t)30000 + ( int16_t)30000); // -5536 printf( "%d\n", ( int16_t)30000 + ( int16_t)30000); // 60000 printf( "%u\n", ( int16_t)30000 + ( int16_t)30000); // 60000 puts( ""); printf( "%hd\n", ( uint16_t)30000 + ( uint16_t)30000); // -5536 printf( "%d\n", ( uint16_t)30000 + ( uint16_t)30000); // 60000 printf( "%u\n", ( uint16_t)30000 + ( uint16_t)30000); // 60000 puts( ""); printf( "%d\n", sizeof( ( int16_t)5 - ( int16_t)7) ); // 4 return 0; }
Правила, по которым приведение осуществляется до или после операции беспощадны по своей бесмысленности. А как ещё можно получить 80000, сложив (uint16_t)40000 и (uint16_t)40000, но при этом не получить того же, складывая типы int16_t у меня в голове не укладывается. Хотя, кажется с этим понятно. Есть ещё вопросы, буду пока разбираться.
Ещё gcc мухлюет, передавая в printf тот тип, который указан в форматной строке, в то время как он должен хотя бы выдавать предупреждение, потому что при попытке вывести одно и то же число через %d и %hd где-то должна возникать ошибка, потому что в рантайме неизвестно, что лежит в списке аргументов, но они нормально выводятся.
В целом складывается впечатление, что операнды приводятся к инту ещё до операции, аналогично тому, как это происходит в Java. Только там об этом поведении все знают, так как нельзя неявно привести int к short, а тут можно, о чём говорит вторая строчка в main.
laMer007
> Вы так говорите, как будто 5-65531 равно 10
Оно и равно, по модулю 65536 )
Zefick
> http://ideone.com/QMIil
> Правила, по которым приведение осуществляется до или после операции беспощадны по своей бесмысленности.
Вы же понимаете, что указывать типы результатов, которых вы не знаете, вручную в printf было очень глупо:
unsigned short c = 100500; // warning cout<< (( int16_t)5 - ( int16_t)7)<<endl; // -2 cout<< ( ( int16_t)5 - ( int16_t)7)<<endl; // -2 cout<< ( ( int16_t)65534 + ( int16_t)0)<<endl; // -2 cout<<endl; cout<< ( ( uint16_t)5 - ( uint16_t)7)<<endl; // -2 cout<< ( ( uint16_t)5 - ( uint16_t)7)<<endl; // -2 cout<< ( ( uint16_t)65534 + ( uint16_t)0)<<endl; // 65534 cout<<endl; cout<< ( ( int16_t)40000 + ( int16_t)40000)<<endl; // -51072 cout<< ( ( int16_t)40000 + ( int16_t)40000)<<endl; // -51072 cout<< ( ( int16_t)40000 + ( int16_t)40000)<<endl; // -51072 cout<<endl; cout<< ( ( uint16_t)40000 + ( uint16_t)40000)<<endl; // 80000 cout<< ( ( uint16_t)40000 + ( uint16_t)40000)<<endl; // 80000 cout<< ( ( uint16_t)40000 + ( uint16_t)40000)<<endl; // 80000 cout<<endl; cout<< ( ( int16_t)30000 + ( int16_t)30000)<<endl; // 60000 cout<< ( ( int16_t)30000 + ( int16_t)30000)<<endl; // 60000 cout<< ( ( int16_t)30000 + ( int16_t)30000)<<endl; // 60000 cout<<endl; cout<< ( ( uint16_t)30000 + ( uint16_t)30000)<<endl; // 60000 cout<< ( ( uint16_t)30000 + ( uint16_t)30000)<<endl; // 60000 cout<< ( ( uint16_t)30000 + ( uint16_t)30000)<<endl; // 60000 cout<< endl; cout<< ( sizeof( ( int16_t)5 - ( int16_t)7) )<<endl; // 4
http://liveworkspace.org/code/51e0e36576c742233f32e503b8bb5e01
laMer007
> Вы так говорите, как будто 5-65531 равно 10
да, равно. старшие разряды выбрасываются т.к. вместимость unsigned short не позволяет их вместить.
0 == 65536 == 2*65536 == ... = 2^(16+n)
В m-битном числе 0 == 2^(m+n)
Zefick
> Правила, по которым приведение осуществляется до или после операции беспощадны
> по своей бесмысленности
правила абсолютно логичны.
> А как ещё можно получить 80000, сложив (uint16_t)40000 и (uint16_t)40000, но
> при этом не получить того же, складывая типы int16_t
в обоих случаях складываются 40000 + 40000, работаем в 32-битной системе, поэтому
0000 0000 0000 0000 1001 1100 0100 0000 40000
+ 0000 0000 0000 0000 1001 1100 0100 0000 40000
= 0000 0000 0000 0001 0011 1000 1000 0000 80000 = 65536 + 14464
ALPINE
> да, равно. старшие разряды выбрасываются
Вы пропагандируете в своей статье насильный кастинг к меньшему типу, как крайне полезный приём, в то время, как результат мы получаем во float. Зачем, спрашивается? (:
>>(int16_t)40000 + (int16_t)40000); // -51072
> Враньё, у меня выводится 80000 и это логично.
http://ideone.com/Ypu45
Что-за у вас убогий компилятор? Вы не поверите, но он не смог откастить 40000 до int16_t:
http://ideone.com/KoK2n
laMer007
> Вы же понимаете, что указывать типы результатов, которых вы не знаете, вручную
> в printf было очень глупо:
к типу, указанному в printf, результаты будут приводиться.
P.S. а cout всё выводит в "обычном int" (signed int, 32 бит)
laMer007
> результат мы получаем во float. Зачем, спрашивается? (:
просто для примера. я в своём проекте упёрся именно в это (и я как раз умножал на флоат-коэффициент) и решил изложить грабли, на которые сам же и наступил.
laMer007
насчёт вранья сорри, всё ок, я перепутал int16_t и uint16_t - буковку "u" пропустил. с этим всё в порядке.
P.S. Проанализировал всё, для меня теперь абсолютно всё понятно и логично. Спрашивайте, разъясню :)
ALPINE
> в обоих случаях складываются 40000 + 40000, работаем в 32-битной системе, поэтому
Я имел в виду случай где результат -51072. А просиходит там следующее: 40000 сначала приводится к short и получается -25536. Затем это приводится к инту и складывается. Теперь всё логично, просто поведение printf запутывает, хотя я и сам тут накосячил. На самом деле компилятор не должен пытаться привести типы и анализировать форматную строку должен максимум для выдачи предупреждений.
Кстати, одному мне кажется, что название подсказки какое-то неправильное? Даже если вставить "неявные приведения", то всё равно что-то не то.
ALPINE
> к типу, указанному в printf, результаты будут приводиться.
Вообще то мы хотели узнать реальный результат и тип выражения, а не указанный вручную.
> к типу, указанному в printf, результаты будут приводиться.
"Только между signed/unsigned в пределах целочисленного типа одного размера". В остальных случаях UB.
> P.S. а cout всё выводит в "обычном int" (signed int, 32 бит)
cout все выводит с тем типом, который реально является типом результата выражения.
Zefick
> Кстати, одному мне кажется, что название подсказки какое-то неправильное?
да... поправил, так вроде лучше.
laMer007
> Вообще то мы хотели узнать реальный результат и тип выражения, а не указанный вручную.
результирующим типом "по умолчанию" всегда будет int
> "Только между signed/unsigned в пределах целочисленного типа одного размера"
signed/unsigned и отсечение старших разрядов.
> cout все выводит с тем типом, который реально является типом результата выражения
...то есть в int :)
P.S. для 64-битных приложений это естественно будет __int64
Тема в архиве.