warning: integer overflow in expression

Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
Закрыто
Родился
Сообщения: 9
Зарегистрирован: Пт дек 12, 2008 23:28:02
Откуда: Україна

Сообщение Andergrin »

Здравствуйте!
Пишу в WinAVR, и компилятор сообщает о переполнении переменной, хотя я объявил ее как long. Код (для проверки подставил значение 4690, которое принимает переменная в моей программе, после чего она умножается на 32):

Код: Выделить всё

#include <avr>
#include <stdio>

long i;

int main(void)
{	
	i=4690*32; //Тут возникает ошибка, i=150080
}
Если ввести вместо i=4690*32; i=150080; то ошибки не возникает. С чем это связано?
Реклама
Потрогал лапой паяльник
Сообщения: 327
Зарегистрирован: Пт фев 08, 2008 13:46:03
Откуда: Воронеж

Сообщение pirotehnick »

Вероятно с тем, что компилятор не может преобразовать операцию умножения 16 разрядного значения на 8 - разрядное, т.к. инструкции умножения поддерживают лишь умножение двух 8-разрядных чисел с 16-разрядным результатом.
Контактная информация:
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

не надо фантазий.
в правой части выражения у вас 2 константы. числовые константы по умолчанию имеют тип int. результат любого выражения (промежуточные результаты вычислений) по умолчанию будет совпадать с int (если все составные части меньше или равны int) или с наибольшим "размером типа" входящих в выражение элементов - в вашем случае int. и лишь после всех вычислений результат преобразуется в тип, необходимый для левой части, т.е. для переменной long

естественно, при умножении int * int результат в int может не поместиться - о чем компилятор вам и говорит. хотите избавиться от проблемы - сделайте так, чтобы какой-то элемент в правой части имел тип long, например так:

Код: Выделить всё

 i = 32L * 40245
т.е. 32L - это константа типа long
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Мучитель микросхем
Сообщения: 459
Зарегистрирован: Вс июн 01, 2008 12:16:38

Сообщение smac »

pirotehnick писал(а):Вероятно с тем, что компилятор не может преобразовать операцию умножения 16 разрядного значения на 8 - разрядное, т.к. инструкции умножения поддерживают лишь умножение двух 8-разрядных чисел с 16-разрядным результатом.
Сильно сомневаюсь, инструкции сложения регистров вообще работают с 8-ми битными операндами, однако с помощью МК можно сложить числа практически любой (ограничено оперативной памятью МК) разрядности, т. е. флаг переноса еще никто не отменял. :)
З. Ы. Ну собственно ARV уже давно все объяснил, просто у меня страница давно была загружена, а добрался я до нее только сейчас.
Последний раз редактировалось smac Сб дек 13, 2008 15:37:22, всего редактировалось 1 раз.
Реклама
Эиком - электронные компоненты и радиодетали
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

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

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Потрогал лапой паяльник
Сообщения: 327
Зарегистрирован: Пт фев 08, 2008 13:46:03
Откуда: Воронеж

Сообщение pirotehnick »

smac писал(а):с помощью МК можно сложить числа практически любой (ограничено оперативной памятью МК) разрядности
Естественно можно, только вот речь шла не о сложении, а об умножении, а там одного флага переноса не достаточно :)

ARV писал(а):я уже говорил - pirotehnick фантазирует.
Я предполагаю...
Тогда попробуй, скомпилить код с фрагментом PORTC = PORTD/PORTB.
и посмотри какая весёлая хрень получается. :)

И вообще из темы не совсем понятно: "warning" - это разве ошибка!?
Последний раз редактировалось pirotehnick Сб дек 13, 2008 17:46:08, всего редактировалось 1 раз.
Контактная информация:
Реклама
Потрогал лапой паяльник
Сообщения: 327
Зарегистрирован: Пт фев 08, 2008 13:46:03
Откуда: Воронеж

Сообщение pirotehnick »

Да, кстате, не совсем понятно, из выражения :"i=4690*32" компилятор, по хорошему, должен был подставить готовую константу. Видимо оптимизация полностью была отключена...
Контактная информация:
Друг Кота
Сообщения: 13796
Зарегистрирован: Чт сен 20, 2007 14:08:00

Сообщение kalobyte »

странные хедеры для винавр
там avr/io.h должно быть
у тебя какая версия?

писать надо unsigned long int и переменная должна быть внутри мейн (если она не глобальная)
Родился
Сообщения: 9
Зарегистрирован: Пт дек 12, 2008 23:28:02
Откуда: Україна

Сообщение Andergrin »

У меня в тексте программы все правильно "avr/io.h"
"stdio.h", это что-то движок форума порезал
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

pirotehnick, в вашем "смешном" примере нет ничего удивительного. вы невнимательно прочитали мое сообщение. повторю: в Си все значения сначала приводятся к типу int (если иное не сказано) или к типу наибольшего элемента, затем производятся вычисления, а затем результат приводится к типу переменной, куда он записывается.

теперь ваш пример PORTC = PORTD/PORTB.
все переменные имеют тип unsigned char. значит, сначала PORTD и PORTB будут приведены к типу int, причем самое смешное в том, что беззнаковый байт будет преобразован в знаковый int, затем будет произведено деление, а затем от результата будет отброшен старший байт, а младший будет выведен в PORTC. надеюсь, вам не надо объяснять, что 0xFF будет превращено таким образом в -1, ну, и потом после деления будет что-то невероятное, а его младший байт - и того удивительнее...

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

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

Мой уютный бложик... заходите!
Контактная информация:
Потрогал лапой паяльник
Сообщения: 327
Зарегистрирован: Пт фев 08, 2008 13:46:03
Откуда: Воронеж

Сообщение 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
//...................

По-хорошему компилятор сам должен определять разрядность значений и генерить соответствующий код.
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

pirotehnick писал(а):Он приводит их к типу int, но только внутри самой программы компилятора. Т.е. если мне допустим нужно сложить PORTD+PORTB и вывести результат в PORTC, то ни к какому 32 разрядному значению он их приводить не должен, а просто сложить два 8-разрядных значения и вывести результат в PORTC, забив, при этом, на переполнение.
Т.е. он должен сгенерить что-то вроде этого:
//...................
add r16, r17
out PORTC r16
//...................

По-хорошему компилятор сам должен определять разрядность значений и генерить соответствующий код.
это вы так считаете. а компилятор обязан следовать требованиям стандарта Си. если он будет делать так, как говорите вы - это будет плохой компилятор.
кроме того вы плоховато представляете себе, какую разрядность имеет int для AVR: вовсе не 32, а всего 16 бит.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Потрогал лапой паяльник
Сообщения: 327
Зарегистрирован: Пт фев 08, 2008 13:46:03
Откуда: Воронеж

Сообщение pirotehnick »

ARV писал(а):это вы так считаете. а компилятор обязан следовать требованиям стандарта Си.
Не я так считаю, а разработчики компилятора:

Изображение

И было бы немного странным, если бы он старался пихать в программу лишний мусор.
ARV писал(а):если он будет делать так, как говорите вы - это будет плохой компилятор.
Не совсем понятно, что вы имели ввиду. Т.е. если он НЕ будет возится ещё с байтом, который не нужен, то это будет плохо?
ARV писал(а):int для AVR: вовсе не 32, а всего 16 бит.
Да, действительно, а я всегда думал, что обычный int только 32 разрядный, видимо сильно привык к виндовскому Си :). Хотя это не совсем вписывается в стандарт Си. А зачем тогда short???
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

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

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

Мой уютный бложик... заходите!
Контактная информация:
Держит паяльник хвостом
Аватара пользователя
Сообщения: 993
Зарегистрирован: Пн сен 18, 2006 11:16:05
Откуда: Тула

Сообщение Neekeetos »

ARV писал(а):я вам уже сказал: есть стандарт Си, и компилятор должен ему следовать. только в этом случае гарантируется, что исходник будет одинаково компилироваться разными компиляторами. если ваш компилятор делает не по стандарту - это нестандартный компилятор, что можно понимамть как синоним "плохой".
Вообще говоря компилятор для исходника указанного в самом начале должен сам вычислить произведение двух констант и подставить при компиляции число, то что он этого не делает а пытается интерпретировать операции( и видимо впихнуть это несчастное умножение в код программы) , является самым настоящим багом, а раз так то и предложить можно только считать это произведение за него до тех пор пока не исправят разработчики винавр.
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение 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 и все...
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Держит паяльник хвостом
Аватара пользователя
Сообщения: 993
Зарегистрирован: Пн сен 18, 2006 11:16:05
Откуда: Тула

Сообщение 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: (Добавлено) На самом деле там макросы отличаются и в старшие байты грузятся нули, это я просмотрел. В любом случае константное выражение не должно приводится к какому то определенному типу данных до непосредственно вычислений, постараюсь найти соответствующие параграфы в стандарте на С.
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение 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)
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Держит паяльник хвостом
Аватара пользователя
Сообщения: 993
Зарегистрирован: Пн сен 18, 2006 11:16:05
Откуда: Тула

Сообщение 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 бит арифметика.
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Neekeetos писал(а):Посмотрел стандарт, обсуждаемый случай называется constant-folding, это оптимизация позволяющая упростить константные выражения на этапе компиляции. Судя по описанию это упрощение делается с использованием базовых типов в тч для целочисленных констант будет использоваться int если у нету явного приведения. Для авр int это 16 бит значение так что похоже компилятор сделал все верно отбросив старшие два байта у результата вычислений тк использовалась 16 бит арифметика.
ну, а я что говорил? ;)
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Закрыто

Вернуться в «Микроконтроллеры и ПЛИС»