Например TDA7294

Форум РадиоКот :: Просмотр темы - warning: integer overflow in expression
Форум РадиоКот
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 фантазирует.

Автор:  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 разрядный, видимо сильно привык к виндовскому Си :). Хотя это не совсем вписывается в стандарт Си. А зачем тогда short???

Автор:  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/