| Форум РадиоКот https://radiokot.ru/forum/ |
|
| warning: integer overflow in expression https://radiokot.ru/forum/viewtopic.php?f=20&t=12327 |
Страница 1 из 1 |
| Автор: | Andergrin [ Пт дек 12, 2008 23:46:45 ] |
| Заголовок сообщения: | warning: integer overflow in expression |
Здравствуйте! Пишу в WinAVR, и компилятор сообщает о переполнении переменной, хотя я объявил ее как long. Код (для проверки подставил значение 4690, которое принимает переменная в моей программе, после чего она умножается на 32): Код: #include <avr>
#include <stdio> long i; int main(void) { i=4690*32; //Тут возникает ошибка, i=150080 } Если ввести вместо i=4690*32; i=150080; то ошибки не возникает. С чем это связано? |
|
| Автор: | pirotehnick [ Сб дек 13, 2008 14:31:27 ] |
| Заголовок сообщения: | |
Вероятно с тем, что компилятор не может преобразовать операцию умножения 16 разрядного значения на 8 - разрядное, т.к. инструкции умножения поддерживают лишь умножение двух 8-разрядных чисел с 16-разрядным результатом. |
|
| Автор: | ARV [ Сб дек 13, 2008 14:53:35 ] |
| Заголовок сообщения: | |
не надо фантазий. в правой части выражения у вас 2 константы. числовые константы по умолчанию имеют тип int. результат любого выражения (промежуточные результаты вычислений) по умолчанию будет совпадать с int (если все составные части меньше или равны int) или с наибольшим "размером типа" входящих в выражение элементов - в вашем случае int. и лишь после всех вычислений результат преобразуется в тип, необходимый для левой части, т.е. для переменной long естественно, при умножении int * int результат в int может не поместиться - о чем компилятор вам и говорит. хотите избавиться от проблемы - сделайте так, чтобы какой-то элемент в правой части имел тип long, например так: Код: i = 32L * 40245 т.е. 32L - это константа типа long
|
|
| Автор: | smac [ Сб дек 13, 2008 15:35:02 ] |
| Заголовок сообщения: | |
pirotehnick писал(а): Вероятно с тем, что компилятор не может преобразовать операцию умножения 16 разрядного значения на 8 - разрядное, т.к. инструкции умножения поддерживают лишь умножение двух 8-разрядных чисел с 16-разрядным результатом.
Сильно сомневаюсь, инструкции сложения регистров вообще работают с 8-ми битными операндами, однако с помощью МК можно сложить числа практически любой (ограничено оперативной памятью МК) разрядности, т. е. флаг переноса еще никто не отменял. З. Ы. Ну собственно ARV уже давно все объяснил, просто у меня страница давно была загружена, а добрался я до нее только сейчас. |
|
| Автор: | ARV [ Сб дек 13, 2008 15:36:51 ] |
| Заголовок сообщения: | |
smac писал(а): pirotehnick писал(а): Вероятно с тем, что компилятор не может преобразовать операцию умножения 16 разрядного значения на 8 - разрядное, т.к. инструкции умножения поддерживают лишь умножение двух 8-разрядных чисел с 16-разрядным результатом. Сильно сомневаюсь, инструкции сложения регистров вообще работают с 8-ми битными операндами, однако с помощью МК можно сложить числа практически любой (ограничено оперативной памятью МК) разрядности, т. е. флаг переноса еще никто не отменял. |
|
| Автор: | pirotehnick [ Сб дек 13, 2008 17:35:01 ] |
| Заголовок сообщения: | |
smac писал(а): с помощью МК можно сложить числа практически любой (ограничено оперативной памятью МК) разрядности Естественно можно, только вот речь шла не о сложении, а об умножении, а там одного флага переноса не достаточно ARV писал(а): я уже говорил - pirotehnick фантазирует.
Я предполагаю... Тогда попробуй, скомпилить код с фрагментом PORTC = PORTD/PORTB. и посмотри какая весёлая хрень получается. И вообще из темы не совсем понятно: "warning" - это разве ошибка!? |
|
| Автор: | pirotehnick [ Сб дек 13, 2008 17:44:37 ] |
| Заголовок сообщения: | |
Да, кстате, не совсем понятно, из выражения :"i=4690*32" компилятор, по хорошему, должен был подставить готовую константу. Видимо оптимизация полностью была отключена... |
|
| Автор: | kalobyte [ Сб дек 13, 2008 20:09:10 ] |
| Заголовок сообщения: | |
странные хедеры для винавр там avr/io.h должно быть у тебя какая версия? писать надо unsigned long int и переменная должна быть внутри мейн (если она не глобальная) |
|
| Автор: | Andergrin [ Сб дек 13, 2008 20:41:27 ] |
| Заголовок сообщения: | |
У меня в тексте программы все правильно "avr/io.h" "stdio.h", это что-то движок форума порезал |
|
| Автор: | ARV [ Сб дек 13, 2008 21:33:21 ] |
| Заголовок сообщения: | |
pirotehnick, в вашем "смешном" примере нет ничего удивительного. вы невнимательно прочитали мое сообщение. повторю: в Си все значения сначала приводятся к типу int (если иное не сказано) или к типу наибольшего элемента, затем производятся вычисления, а затем результат приводится к типу переменной, куда он записывается. теперь ваш пример PORTC = PORTD/PORTB. все переменные имеют тип unsigned char. значит, сначала PORTD и PORTB будут приведены к типу int, причем самое смешное в том, что беззнаковый байт будет преобразован в знаковый int, затем будет произведено деление, а затем от результата будет отброшен старший байт, а младший будет выведен в PORTC. надеюсь, вам не надо объяснять, что 0xFF будет превращено таким образом в -1, ну, и потом после деления будет что-то невероятное, а его младший байт - и того удивительнее... что касается удивления в вычислении констаны. да, компилятор подставит конкретное число. только это число получается с переполнением int - вот он и предупреждает, что хрень какая-то будет в итоге (типа как в вашем примере с портами). warning - это не ошибка, а предупреждение. но в некоторых случах это хуже ошибки. предупреждение может означать, что программа будет работать, но как именно - неизвестно. и в данном случае смысл как раз такой. |
|
| Автор: | pirotehnick [ Сб дек 13, 2008 22:49:48 ] |
| Заголовок сообщения: | |
ARV писал(а): в Си все значения сначала приводятся к типу int Может я конечно немного не по теме выразился, но я говрил о другом. Я имел ввиду, то, во что конкретно компилятор переведёт эту строчку, т.е. какой ассемблерный код он выдаст. Когда я написал что-то подобное авиеровский компилер всё это благополучно скушал, но вот что получилось в итоге... это жуть. В итоге я ни чего не получил на выходе, т.к. программа просто зациклилась на каком-то переходе. Т.е. я хочу сказать, что не стоит так легкомысленно относиться к сишному компилятору для AVR. Всё же у Си нет должной совместимости с инструкциями ядра AVR. Ну и вот эта весёлая хрень у меня и получилась. С этих пор все свои проги для AVR я пишу только на Асьме и очень им доволен и всё работает как часы. ARV писал(а): все переменные имеют тип unsigned char. значит, сначала PORTD и PORTB будут приведены к типу int
Он приводит их к типу int, но только внутри самой программы компилятора. Т.е. если мне допустим нужно сложить PORTD+PORTB и вывести результат в PORTC, то ни к какому 32 разрядному значению он их приводить не должен, а просто сложить два 8-разрядных значения и вывести результат в PORTC, забив, при этом, на переполнение. Т.е. он должен сгенерить что-то вроде этого: //................... in r16, PORTB in r17, PORTD add r16, r17 out PORTC r16 //................... По-хорошему компилятор сам должен определять разрядность значений и генерить соответствующий код. |
|
| Автор: | ARV [ Сб дек 13, 2008 22:55:15 ] |
| Заголовок сообщения: | |
pirotehnick писал(а): Он приводит их к типу int, но только внутри самой программы компилятора. Т.е. если мне допустим нужно сложить PORTD+PORTB и вывести результат в PORTC, то ни к какому 32 разрядному значению он их приводить не должен, а просто сложить два 8-разрядных значения и вывести результат в PORTC, забив, при этом, на переполнение. это вы так считаете. а компилятор обязан следовать требованиям стандарта Си. если он будет делать так, как говорите вы - это будет плохой компилятор.
Т.е. он должен сгенерить что-то вроде этого: //................... add r16, r17 out PORTC r16 //................... По-хорошему компилятор сам должен определять разрядность значений и генерить соответствующий код. кроме того вы плоховато представляете себе, какую разрядность имеет int для AVR: вовсе не 32, а всего 16 бит. |
|
| Автор: | pirotehnick [ Вс дек 14, 2008 01:18:32 ] |
| Заголовок сообщения: | |
ARV писал(а): это вы так считаете. а компилятор обязан следовать требованиям стандарта Си. Не я так считаю, а разработчики компилятора: ![]() И было бы немного странным, если бы он старался пихать в программу лишний мусор. ARV писал(а): если он будет делать так, как говорите вы - это будет плохой компилятор. Не совсем понятно, что вы имели ввиду. Т.е. если он НЕ будет возится ещё с байтом, который не нужен, то это будет плохо? ARV писал(а): int для AVR: вовсе не 32, а всего 16 бит.
Да, действительно, а я всегда думал, что обычный int только 32 разрядный, видимо сильно привык к виндовскому Си |
|
| Автор: | ARV [ Вс дек 14, 2008 08:50:59 ] |
| Заголовок сообщения: | |
я вам уже сказал: есть стандарт Си, и компилятор должен ему следовать. только в этом случае гарантируется, что исходник будет одинаково компилироваться разными компиляторами. если ваш компилятор делает не по стандарту - это нестандартный компилятор, что можно понимамть как синоним "плохой". в стандарте сказано: преобразуются в int - значит, байт обязан стать int-ом перед тем, как поучаствовать в вычислениях. после всего оптимизатор может выкинуть лишние команды, но только при условии, что результат однозначно будет правильным. в случае с умножениям (делением) байтов этого, оччевидно, утверждать нельзя. но вы можете оставаться при своем мнении - ничего, кроме ваших проблем, это не даст. |
|
| Автор: | Neekeetos [ Пн дек 15, 2008 13:06:55 ] |
| Заголовок сообщения: | |
ARV писал(а): я вам уже сказал: есть стандарт Си, и компилятор должен ему следовать. только в этом случае гарантируется, что исходник будет одинаково компилироваться разными компиляторами. если ваш компилятор делает не по стандарту - это нестандартный компилятор, что можно понимамть как синоним "плохой".
Вообще говоря компилятор для исходника указанного в самом начале должен сам вычислить произведение двух констант и подставить при компиляции число, то что он этого не делает а пытается интерпретировать операции( и видимо впихнуть это несчастное умножение в код программы) , является самым настоящим багом, а раз так то и предложить можно только считать это произведение за него до тех пор пока не исправят разработчики винавр. |
|
| Автор: | ARV [ Пн дек 15, 2008 18:02:15 ] |
| Заголовок сообщения: | |
Neekeetos писал(а): Вообще говоря компилятор для исходника указанного в самом начале должен сам вычислить произведение двух констант и подставить при компиляции число, то что он этого не делает а пытается интерпретировать операции( и видимо впихнуть это несчастное умножение в код программы) , является самым настоящим багом, а раз так то и предложить можно только считать это произведение за него до тех пор пока не исправят разработчики винавр. бред какой-то говорите... компилятор предупреждает, что результат выражения выходит за пределы int. этот результат получается на этапе компиляции - где тут баг?! все верно.
возьмем пример: char k = 150 * 2; в char никак не может быть значение 300. поэтому компилятор вычислит реальное значнеие результата 0x12C и поместит в переменную k младший байт 0x2C, при этом он выдаст warning ранее указанного содержания - результат получается с переполнением. вы считаете, тут какое-то нарушение стандарта или логики?! пример, что я привел - упрощенный. однако от long k = 6000 * 300; он отличается очень немного! если вы думаете, что раз "по-человечески" результат умножения помещается в 32 бита, то и "по-компиляторному" так быть должно - вот это как раз неверно. иначе, почему бы всегда тактовая частота в константе-макросе F_CPU всегда записывается с суффиксом UL: #define F_CPU 1000000UL? по-вашему, хватило бы просто указания 1000000 и все... |
|
| Автор: | Neekeetos [ Пн дек 15, 2008 19:13:47 ] |
| Заголовок сообщения: | |
ARV писал(а): Neekeetos писал(а): Вообще говоря компилятор для исходника указанного в самом начале должен сам вычислить произведение двух констант и подставить при компиляции число, то что он этого не делает а пытается интерпретировать операции( и видимо впихнуть это несчастное умножение в код программы) , является самым настоящим багом, а раз так то и предложить можно только считать это произведение за него до тех пор пока не исправят разработчики винавр. бред какой-то говорите... компилятор предупреждает, что результат выражения выходит за пределы int. этот результат получается на этапе компиляции - где тут баг?! все верно.Вот то что я скомпилил винавр, gcc 4.1.2, Код: long i; int main(void) { i=4690*32; //Тут возникает ошибка, i=150080 return i; } Что мы ожидаем от безглючного компилятора? что он положит какое то число в переменную i а затем поместит эту переменную в стэк? Так и происходит вот что он выдал как результат компиляции с вышеуказанной в треде ошибкой: Код: .file "test-test.c"
.arch avr2 __SREG__ = 0x3f __SP_H__ = 0x3e __SP_L__ = 0x3d __tmp_reg__ = 0 __zero_reg__ = 1 .global __do_copy_data .global __do_clear_bss .text .global main .type main, @function main: /* prologue: frame size=0 */ push r28 push r29 in r28,__SP_L__ in r29,__SP_H__ /* prologue end (size=4) */ ldi r24,lo8(19008) ldi r25,hi8(19008) ldi r26,hlo8(19008) ldi r27,hhi8(19008) sts i,r24 sts (i)+1,r25 sts (i)+2,r26 sts (i)+3,r27 lds r24,i lds r25,(i)+1 lds r26,(i)+2 lds r27,(i)+3 /* epilogue: frame size=0 */ pop r29 pop r28 ret /* epilogue end (size=3) */ /* function main size 27 (20) */ .size main, .-main .comm i,4,1 /* File "test-test.c": code 27 = 0x001b ( 20), prologues 4, epilogues 3 */ Ожидаем что загрузится результат умножения который 24A40, видим в действительности в обе половины 4хбайтного числа грузится только 4A40, двоечка отброшена. Я в принципе соглашусь что раз компилятору так трудно работать с 32битным числом, он отбросил старшие два байта (хоть это и неверно, должно быть понятное дело 00 02), но вот то что он вместо старших байтов- нулей загрузил еще раз 4A40 - это баг. Откуда он взялся? Оттуда что компилятор неверно обрабатывает константные выражения. По поводу бреда в моем сообщении пожалуйста поподробнее, я не совсем вас понимаю PS: (Добавлено) На самом деле там макросы отличаются и в старшие байты грузятся нули, это я просмотрел. В любом случае константное выражение не должно приводится к какому то определенному типу данных до непосредственно вычислений, постараюсь найти соответствующие параграфы в стандарте на С. |
|
| Автор: | ARV [ Пн дек 15, 2008 19:47:54 ] |
| Заголовок сообщения: | |
не знаю, откуда у вас такие результаты... вот листинг, который получил я (оптимизация -Os): Код: int main(void) как видите, никакого дубля младшего слова в старшем нет.
{ i=4690*32; //Тут возникает ошибка, i=150080 5e: 80 e4 ldi r24, 0x40 ; 64 60: 9a e4 ldi r25, 0x4A ; 74 62: a0 e0 ldi r26, 0x00 ; 0 64: b0 e0 ldi r27, 0x00 ; 0 return i; 66: 80 93 60 00 sts 0x0060, r24 6a: 90 93 61 00 sts 0x0061, r25 6e: a0 93 62 00 sts 0x0062, r26 72: b0 93 63 00 sts 0x0063, r27 } 76: 80 e4 ldi r24, 0x40 ; 64 78: 9a e4 ldi r25, 0x4A ; 74 7a: 08 95 ret по поводу макросов. я и не говорил, что макрос предъявляет какие-то требования к типам. макрос - это всего лишь подстановка текста, реально значение константных выражений вычисляется при компиляции. возможно, часть выражений вычисляется при обработке препроцессором - тут я точно не скажу, надо смотреть документацию. однако, для числовой константы по умолчанию используется тип int (который используется по умолчанию и для любой переменной) - это факт. да, компилятор AVR-GCC 4.3.0 (WinAVR 20080610) |
|
| Автор: | Neekeetos [ Пн дек 15, 2008 20:44:03 ] |
| Заголовок сообщения: | |
ARV писал(а): не знаю, откуда у вас такие результаты... вот листинг, Результаты - то что сам gcc нагенерил в виде исходника ассемблерного (avr-gcc.exe -save-temps *.с) ARV писал(а): 7a: 08 95 ret[/code]как видите, никакого дубля младшего слова в старшем нет. Нету, я и сам это заметил поправив свое сообщение. ARV писал(а): возможно, часть выражений вычисляется при обработке препроцессором - тут я точно не скажу, надо смотреть документацию. однако, для числовой константы по умолчанию используется тип int (который используется по умолчанию и для любой переменной) - это факт.
да, компилятор AVR-GCC 4.3.0 (WinAVR 20080610) Посмотрел стандарт, обсуждаемый случай называется constant-folding, это оптимизация позволяющая упростить константные выражения на этапе компиляции. Судя по описанию это упрощение делается с использованием базовых типов в тч для целочисленных констант будет использоваться int если у нету явного приведения. Для авр int это 16 бит значение так что похоже компилятор сделал все верно отбросив старшие два байта у результата вычислений тк использовалась 16 бит арифметика. |
|
| Автор: | ARV [ Пн дек 15, 2008 20:52:11 ] |
| Заголовок сообщения: | |
Neekeetos писал(а): Посмотрел стандарт, обсуждаемый случай называется constant-folding, это оптимизация позволяющая упростить константные выражения на этапе компиляции. Судя по описанию это упрощение делается с использованием базовых типов в тч для целочисленных констант будет использоваться int если у нету явного приведения. Для авр int это 16 бит значение так что похоже компилятор сделал все верно отбросив старшие два байта у результата вычислений тк использовалась 16 бит арифметика. ну, а я что говорил? |
|
| Страница 1 из 1 | Часовой пояс: UTC + 3 часа |
| Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |
|



