Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: IAR C умножение int x int = long
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Д_М
Здравствуйте!
Перенёс код алгоритма, отлизанный при помощи C Builder, на IAR - не работает. После долгих поисков выявил, что когда умножаются два числа int, а частное long, старшие 16 бит частного всегда равны нулю. Когда сделал множителе тоже long всё нормально. Как-то странно. Другие компиляторы наоборот выдают придупреждения, когда разрядность частного не больше, чем разрядность множителей - char/int, или int/long...
_Артём_
Цитата(Д_М @ Jun 23 2013, 00:01) *
Перенёс код алгоритма, отлизанный при помощи C Builder, на IAR - не работает. После долгих поисков выявил, что когда умножаются два числа int, а частное long, старшие 16 бит частного всегда равны нулю. Когда сделал множителе тоже long всё нормально. Как-то странно. Другие компиляторы наоборот выдают придупреждения, когда разрядность частного не больше, чем разрядность множителей - char/int, или int/long...

Не совсем понятно у вас сам алгоритм описан, лучше бы код привели.

Цитата(Д_М @ Jun 23 2013, 00:01) *
Когда сделал множителе тоже long всё нормально. Как-то странно. Другие компиляторы наоборот выдают придупреждения, когда разрядность частного не больше, чем разрядность множителей - char/int, или int/long...

Когда вы писали программу на Билдере, то видимо вы не учли то обстоятельство что x86 - 32/64 -битная система, а АВР - 8-битная. И тип int у них разный. 4 байта для 86-ых и 2 байта для АВР.
Если хотите чтобы работало и на ПК и на МК, то можно использовать типы из stdint.h или смотреть в описаниях как определены типы для разных архитектур.
Простой вариант. Использовать не int, а short и long.
Д_М
Только что проверил работу программы на AVR. Работает точно также, как на C Builder. За исключением того, что две переменные изменил с int на long. До того не работало. Если я правильно понимаю, то разрядность переменных определяется спецификацией языка C, а не аппаратной платформой, или компилятором. Везде и всегда int -16 бит, long - 32 бита.
_Артём_
Цитата(Д_М @ Jun 23 2013, 00:39) *
Только что проверил работу программы на AVR. Работает точно также, как на C Builder. За исключением того, что две переменные изменил с int на long.

Потому что long на обеих платформах - 4 байта.

Цитата(Д_М @ Jun 23 2013, 00:39) *
Если я правильно понимаю, то разрядность переменных определяется спецификацией языка C, а не аппаратной платформой, или компилятором.

Нет, разрядность зависит от платформы/компилятора.

Цитата(Д_М @ Jun 23 2013, 00:39) *
Везде и всегда int -16 бит

У вас же программа на ПК работала? Значил там int - 32 бита. На IAR AVR перестала, потому что int - 16 бит. Смотрите про int в EWAVR_CompilerReference пункт "Basic data types".


Цитата(Д_М @ Jun 23 2013, 00:39) *
Везде и всегда long - 32 бита.

Везде-не везде, но на ПК и АВР в этом в случае одинаково.
KRS
Цитата(Д_М @ Jun 23 2013, 01:01) *
что когда умножаются два числа int, а частное long, старшие 16 бит частного всегда равны нулю.

при умножении получается произведение, причем здесь частное? (частное образуется при делении)

все верно при умножении двух int получается int, результат преобразуется в long!
Но только для i386 int 32 битный, а для AVR - 16 битный! Поэтому старшие 16 бит будут или 0 или 0xffff (если результат отрицательный)



Цитата(Д_М @ Jun 23 2013, 01:39) *
Если я правильно понимаю, то разрядность переменных определяется спецификацией языка C, а не аппаратной платформой, или компилятором. Везде и всегда int -16 бит, long - 32 бита.

Это не так и во всех книжках по С это написано.


Кстати для AMD64 вообще:
linux - long 64 бита, а windows - long 32 бита!
Так что где важна битность - используйте stdint.h
Xenia
Цитата(Д_М @ Jun 23 2013, 01:01) *
После долгих поисков выявил, что когда умножаются два числа int, а частное long, старшие 16 бит частного всегда равны нулю. Когда сделал множителе тоже long всё нормально. Как-то странно. Другие компиляторы наоборот выдают придупреждения, когда разрядность частного не больше, чем разрядность множителей - char/int, или int/long...


1. При умножении чисел частное получиться не может. Частное есть результат деления (делимого на делитель), а результат умножения (сомножителей) называется произведением.

2. Компиляторы выдают предупреждение, когда результат усекается, когда результат "шире", чем та переменая, в которою его велено засунуть. А ваш случай противопоположный - int-результат спокойно помещается в long-переменную. С чего бы компилятору этим возмущаться? Ведь опасной для расчета потери части результата от этого произойти не может.

3. По законам языка C результат арифметической операции не превышает разрядности самого длинного операнда. Поэтому результат умножени int x int тоже будет получен в разрядной сетке int, даже, если она для него тесна. Иными словами, разрядность произведения зависит только от разрядности сомножителей, но никак не от разярдности переменной, у которою то произведение в последствии кладут. Поэтому для получения long-резултата необходимо хотя бы один из сомножителей привести к типу long, хотя бы явным образом: lres = (long)i1 * i2

4. Таких проблем нет, когда разрядность int равна разрядности long. Именно это случай имеет место при компиляции 32-разрядных приложений под x86.
mmor
Раз уж про умножение задам сюда же вопрос.
микроконтроллер AT90USB128. Среда программирования JAR 4.10. Язык С++.
есть необходимость помножить целое чесло на число с точкой. Задаю тип переменной double - компилятор комплирует программу, и даже результат умножения близок к истинному. Вот только сомнения меня гложут можно ли такую программу в контроллер зашивать? будет ли работать.
Поэтому все переменные оставил int, но умножаю на число с точкой.
Например:B = A*0.8;
Пусть A = 25000
Тогда B получается 20000.
Т. е. вычисляется вполне точно.
Стал смотреть дизасемблер, а там какой то сложный итеративный процесс идет.
Может кто нибудь на словах объяснить что контроллер делает при умножении на число с точкой?

Ну и еще вопрос по умножению целого на целое.
скажем 0A0Bh x 000Ch = 7884h.
При этом дизасемблер показывает, что умножение происходит по такому алгоритму:

(00x0B)+(0Cx0A) - это младший байт
(0Сx0B) - старший байт результата умножения.

Может кто объяснить откуда такой алгоритм взялся?
Xenia
Цитата(mmor @ Dec 9 2013, 17:14) *
микроконтроллер AT90USB128. Среда программирования JAR 4.10. Язык С++.
есть необходимость помножить целое чесло на число с точкой. Задаю тип переменной double - компилятор комплирует программу, и даже результат умножения близок к истинному. Вот только сомнения меня гложут можно ли такую программу в контроллер зашивать? будет ли работать.
...
Стал смотреть дизасемблер, а там какой то сложный итеративный процесс идет.
Может кто нибудь на словах объяснить что контроллер делает при умножении на число с точкой?


У микроконтроллера AT90USB128 (как впрочем и у остальных МК с архитектурой AVR) отсутствует аппаратное умножение с плавающей точкой, а потому такие операции производятся библиотекой эмуляции. Очень медленно и кода тоже много.

Для большинства дел, связанных со шкалированием (умножения целых чисел на постоянный нецелый коэффициент) бывает достаточно заменить float-множитель на целочисленную дробь. Например, вместо умножения на 0.8 умножать на 4, потом делить на 5.

Но если медленное умножение вас не тяготит, то можете смело довериться компилятору - тут он не ошибется, т.к. не сам сочиняет алгоритм, а пользуется библиотечным, который давным давно отлажен.
mmor
а насчет целочисленного умножения что нибудь подскажете?
Xenia
Цитата(Д_М @ Jun 23 2013, 01:01) *
После долгих поисков выявил, что когда умножаются два числа int, а частное long, старшие 16 бит частного всегда равны нулю. Когда сделал множителе тоже long всё нормально. Как-то странно. Другие компиляторы наоборот выдают придупреждения, когда разрядность частного не больше, чем разрядность множителей - char/int, или int/long...


Это правила языка C таковы - операции над int-операндами он делает в разрядной сетке int, и не обязан расширять результат до long, даже если тот в int не помещается. Другое дело, если один из операндов уже long, тогда и второй операнд тоже будет приведен к типу long, и операция между ними будет произведена в разрядной сетке long. Т.е. забота о типах - это дело программиста, а не компилятора. И если результат умножения двух int-сомножителей требуется перемножать, как long, то следует хотя бы один из сомножителей конвертировать в long-тип явно. Например, так:
Код
int m=20000, n=30000;
long result = (long)m*n;

Причем, понимать эту запись надо не так, что преобразование типа (long) конвертирует результат умножения m*n в long, а так, что оно конвертирует первый сомножитель m из типа int в тип long, после чего компилятор обязан будет их перемножать, как long'и.

Далее. Вы ошибаетесь, в утверждении "другие компиляторы выдают придупреждения, когда разрядность частного не больше, чем разрядность множителей - char/int, или int/long...". Предупреждения выдаются в противоположном случае - когда результат операции не лезет туда, куда его пытаются положить. Вот тогда компилятор и предупреждает о вынужденном округлении или усечении результата. А присваивание короткого длинному вполне законно.

Случай
int = int/long
выдаст предупреждение, но исключением он не является, т.к. здесь один из операндов деления (делитель) имеет тип long, что, в соответствии с правилами, приведет к выполнению деления в разрядной сетке long. По этой причине и результат деления тоже получится long. А засовывание long-результата (частного) в int будет сопровожаться предупреждением о вынужденном урезании. По той же причине результат char/int назад в char без предупреждения не полезет, поскольку будет формально считаться уже int'ом.

Рекомендация вам будет такая: обращайте внимание на то, какого типа у вас операнды. Именно это определяет разрядную сетку, в которой произойдет операция, а вовсе не то, большие числа у вас там лежат или маленькие. Ведь это же компилятор, а не интерпретатор, а потому он компилирует, заранее не анализируя, какие там получатся числа. Об этом должен думать программист, а не компилятор. Например:
Код
int m=20000
long n=1000;
long result = m/n;

Такая запись предупреждения не вызовет, но откомпилируется неоптимально (долго для 8-битников), т.к. делитель типа long обязывает компилятор и делимое тоже превратить в long, а потом делить их друг на друга в разрядной сетке long. Но вы-то понимаете, что результат m/n целочисленен и длиннее int быть не может. Тогда вы должны подсказать компилятору, что n у вас короче long'а:
Код
int m=20000
long n=1000;
long result = m/(int)n;

В последней записи вы сами явно усекаете n до int, чем разрешаете компилятору делить в разрядной сетке int (более быстро).

И наконец. Для умножения char на char с получением результата типа int в компиляторе IAR существует предельно эффективный способ, минуя необходимость совершать умножение в разрядной сетке int. Речь идет встроенных функциях
__intrinsic unsigned int __multiply_unsigned(unsigned char, unsigned char);
__intrinsic signed int __multiply_signed(signed char, signed char);
__intrinsic signed int __multiply_signed_with_unsigned(signed char, unsigned char);
описанных в хидере intrinsics.h
Эти функции позволяют при умножении обходить правила языка C для бинарных операций. Однако для деления таких функций нет.
Сергей Борщ
Цитата(mmor @ Dec 9 2013, 15:14) *
Ну и еще вопрос по умножению целого на целое.
скажем 0A0Bh x 000Ch = 7884h.
При этом дизасемблер показывает, что умножение происходит по такому алгоритму:

(00x0B)+(0Cx0A) - это младший байт
(0Сx0B) - старший байт результата умножения.

Может кто объяснить откуда такой алгоритм взялся?
Вы попутали младший и старший байты результата.
пусть a=0x0A, b=0x0B, c=0x00, d=0x0C
тогда 0x0A0B = (a * 256 + b ), 0x000C = (c * 256 + d)
0x0A0B * 0x000C = (a * 256+b ) * (c * 256 + d) =
a * 256 * c * 256 + b * c * 256 + a * 256 * d + b * d =
(a * c) * 256^2 + (b * c + a * d) * 256 + b * d
-----------------
(a*c)*256^2 вылезло за разрядную сетку int, его нужно отбрасывать и можнго не считать
(b*c+a*d)*256 - дают старший байт (b*c+a*d), или 0x0B*0x00 + 0x0A*0x0C
b*d - младший байт, или 0x0B * 0x0C

Никакого мошенничества.
mmor
да, напутал. уже разобрался в принципе умножения. как всегда все весьма просто)
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.