Страница 1 из 1
warning: integer overflow in expression
Добавлено: Пт дек 12, 2008 23:46:45
Andergrin
Здравствуйте!
Пишу в WinAVR, и компилятор сообщает о переполнении переменной, хотя я объявил ее как long. Код (для проверки подставил значение 4690, которое принимает переменная в моей программе, после чего она умножается на 32):
Код: Выделить всё
#include <avr>
#include <stdio>
long i;
int main(void)
{
i=4690*32; //Тут возникает ошибка, i=150080
}
Если ввести вместо i=4690*32; i=150080; то ошибки не возникает. С чем это связано?
Добавлено: Сб дек 13, 2008 14:31:27
pirotehnick
Вероятно с тем, что компилятор не может преобразовать операцию умножения 16 разрядного значения на 8 - разрядное, т.к. инструкции умножения поддерживают лишь умножение двух 8-разрядных чисел с 16-разрядным результатом.
Добавлено: Сб дек 13, 2008 14:53:35
ARV
не надо фантазий.
в правой части выражения у вас 2 константы. числовые константы по умолчанию имеют тип
int. результат любого выражения (промежуточные результаты вычислений) по умолчанию будет совпадать с
int (если все составные части меньше или равны
int) или с наибольшим "размером типа" входящих в выражение элементов - в вашем случае
int. и лишь после всех вычислений результат преобразуется в тип, необходимый для левой части, т.е. для переменной
long
естественно, при умножении
int * int результат в
int может не поместиться - о чем компилятор вам и говорит. хотите избавиться от проблемы - сделайте так, чтобы какой-то элемент в правой части имел тип
long, например так:
т.е. 32
L - это константа типа
long
Добавлено: Сб дек 13, 2008 15:35:02
smac
pirotehnick писал(а):Вероятно с тем, что компилятор не может преобразовать операцию умножения 16 разрядного значения на 8 - разрядное, т.к. инструкции умножения поддерживают лишь умножение двух 8-разрядных чисел с 16-разрядным результатом.
Сильно сомневаюсь, инструкции сложения регистров вообще работают с 8-ми битными операндами, однако с помощью МК можно сложить числа практически любой (ограничено оперативной памятью МК) разрядности, т. е. флаг переноса еще никто не отменял.

З. Ы. Ну собственно
ARV уже давно все объяснил, просто у меня страница давно была загружена, а добрался я до нее только сейчас.
Добавлено: Сб дек 13, 2008 15:36:51
ARV
smac писал(а):pirotehnick писал(а):Вероятно с тем, что компилятор не может преобразовать операцию умножения 16 разрядного значения на 8 - разрядное, т.к. инструкции умножения поддерживают лишь умножение двух 8-разрядных чисел с 16-разрядным результатом.
Сильно сомневаюсь, инструкции сложения регистров вообще работают с 8-ми битными операндами, однако с помощью МК можно сложить числа практически любой (ограничено оперативной памятью МК) разрядности, т. е. флаг переноса еще никто не отменял.

я уже говорил -
pirotehnick фантазирует.
Добавлено: Сб дек 13, 2008 17:35:01
pirotehnick
smac писал(а):с помощью МК можно сложить числа практически любой (ограничено оперативной памятью МК) разрядности
Естественно можно, только вот речь шла не о сложении, а об умножении, а там одного флага переноса не достаточно
ARV писал(а):я уже говорил - pirotehnick фантазирует.
Я предполагаю...
Тогда попробуй, скомпилить код с фрагментом PORTC = PORTD/PORTB.
и посмотри какая весёлая хрень получается.
И вообще из темы не совсем понятно: "warning" - это разве ошибка!?
Добавлено: Сб дек 13, 2008 17:44:37
pirotehnick
Да, кстате, не совсем понятно, из выражения :"i=4690*32" компилятор, по хорошему, должен был подставить готовую константу. Видимо оптимизация полностью была отключена...
Добавлено: Сб дек 13, 2008 20:09:10
kalobyte
странные хедеры для винавр
там avr/io.h должно быть
у тебя какая версия?
писать надо unsigned long int и переменная должна быть внутри мейн (если она не глобальная)
Добавлено: Сб дек 13, 2008 20:41:27
Andergrin
У меня в тексте программы все правильно "avr/io.h"
"stdio.h", это что-то движок форума порезал
Добавлено: Сб дек 13, 2008 21:33:21
ARV
pirotehnick, в вашем "смешном" примере нет ничего удивительного. вы невнимательно прочитали мое сообщение. повторю: в Си все значения сначала приводятся к типу int (если иное не сказано) или к типу наибольшего элемента, затем производятся вычисления, а затем результат приводится к типу переменной, куда он записывается.
теперь ваш пример PORTC = PORTD/PORTB.
все переменные имеют тип unsigned char. значит, сначала PORTD и PORTB будут приведены к типу int, причем самое смешное в том, что беззнаковый байт будет преобразован в знаковый int, затем будет произведено деление, а затем от результата будет отброшен старший байт, а младший будет выведен в PORTC. надеюсь, вам не надо объяснять, что 0xFF будет превращено таким образом в -1, ну, и потом после деления будет что-то невероятное, а его младший байт - и того удивительнее...
что касается удивления в вычислении констаны. да, компилятор подставит конкретное число. только это число получается с переполнением int - вот он и предупреждает, что хрень какая-то будет в итоге (типа как в вашем примере с портами).
warning - это не ошибка, а предупреждение. но в некоторых случах это хуже ошибки. предупреждение может означать, что программа будет работать, но как именно - неизвестно. и в данном случае смысл как раз такой.
Добавлено: Сб дек 13, 2008 22:49:48
pirotehnick
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
//...................
По-хорошему компилятор сам должен определять разрядность значений и генерить соответствующий код.
Добавлено: Сб дек 13, 2008 22:55:15
ARV
pirotehnick писал(а):Он приводит их к типу int, но только внутри самой программы компилятора. Т.е. если мне допустим нужно сложить PORTD+PORTB и вывести результат в PORTC, то ни к какому 32 разрядному значению он их приводить не должен, а просто сложить два 8-разрядных значения и вывести результат в PORTC, забив, при этом, на переполнение.
Т.е. он должен сгенерить что-то вроде этого:
//...................
add r16, r17
out PORTC r16
//...................
По-хорошему компилятор сам должен определять разрядность значений и генерить соответствующий код.
это
вы так считаете. а компилятор обязан следовать требованиям стандарта Си. если он будет делать так, как говорите вы - это будет плохой компилятор.
кроме того вы плоховато представляете себе, какую разрядность имеет int для AVR: вовсе не 32, а всего 16 бит.
Добавлено: Вс дек 14, 2008 01:18:32
pirotehnick
ARV писал(а):это вы так считаете. а компилятор обязан следовать требованиям стандарта Си.
Не я так считаю, а разработчики компилятора:
И было бы немного странным, если бы он старался пихать в программу лишний мусор.
ARV писал(а):если он будет делать так, как говорите вы - это будет плохой компилятор.
Не совсем понятно, что вы имели ввиду. Т.е. если он
НЕ будет возится ещё с байтом, который не нужен, то это будет плохо?
ARV писал(а):int для AVR: вовсе не 32, а всего 16 бит.
Да, действительно, а я всегда думал, что обычный int только 32 разрядный, видимо сильно привык к виндовскому Си

. Хотя это не совсем вписывается в стандарт Си. А зачем тогда short???
Добавлено: Вс дек 14, 2008 08:50:59
ARV
я вам уже сказал: есть стандарт Си, и компилятор должен ему следовать. только в этом случае гарантируется, что исходник будет одинаково компилироваться разными компиляторами. если ваш компилятор делает не по стандарту - это нестандартный компилятор, что можно понимамть как синоним "плохой".
в стандарте сказано: преобразуются в int - значит, байт обязан стать int-ом перед тем, как поучаствовать в вычислениях. после всего оптимизатор может выкинуть лишние команды, но только при условии, что результат однозначно будет правильным. в случае с умножениям (делением) байтов этого, оччевидно, утверждать нельзя.
но вы можете оставаться при своем мнении - ничего, кроме ваших проблем, это не даст.
Добавлено: Пн дек 15, 2008 13:06:55
Neekeetos
ARV писал(а):я вам уже сказал: есть стандарт Си, и компилятор должен ему следовать. только в этом случае гарантируется, что исходник будет одинаково компилироваться разными компиляторами. если ваш компилятор делает не по стандарту - это нестандартный компилятор, что можно понимамть как синоним "плохой".
Вообще говоря компилятор для исходника указанного в самом начале должен сам вычислить произведение двух констант и подставить при компиляции число, то что он этого не делает а пытается интерпретировать операции( и видимо впихнуть это несчастное умножение в код программы) , является самым настоящим багом, а раз так то и предложить можно только считать это произведение за него до тех пор пока не исправят разработчики винавр.
Добавлено: Пн дек 15, 2008 18:02:15
ARV
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 и все...
Добавлено: Пн дек 15, 2008 19:13:47
Neekeetos
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: (Добавлено) На самом деле там макросы отличаются и в старшие байты грузятся нули, это я просмотрел. В любом случае константное выражение не должно приводится к какому то определенному типу данных до непосредственно вычислений, постараюсь найти соответствующие параграфы в стандарте на С.
Добавлено: Пн дек 15, 2008 19:47:54
ARV
не знаю, откуда у вас такие результаты... вот листинг, который получил я (оптимизация -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)
Добавлено: Пн дек 15, 2008 20:44:03
Neekeetos
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 бит арифметика.
Добавлено: Пн дек 15, 2008 20:52:11
ARV
Neekeetos писал(а):Посмотрел стандарт, обсуждаемый случай называется constant-folding, это оптимизация позволяющая упростить константные выражения на этапе компиляции. Судя по описанию это упрощение делается с использованием базовых типов в тч для целочисленных констант будет использоваться int если у нету явного приведения. Для авр int это 16 бит значение так что похоже компилятор сделал все верно отбросив старшие два байта у результата вычислений тк использовалась 16 бит арифметика.
ну, а я что говорил?
