|
|
  |
IAR C умножение int x int = long, Странность компилятора, или я что-то не допонимаю |
|
|
|
Jun 22 2013, 21:18
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(Д_М @ 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.
|
|
|
|
|
Jun 22 2013, 22:00
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(Д_М @ 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 бита. Везде-не везде, но на ПК и АВР в этом в случае одинаково.
|
|
|
|
|
Jun 22 2013, 22:14
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555

|
Цитата(Д_М @ 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
|
|
|
|
|
Jun 23 2013, 08:14
|

Гуру
     
Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237

|
Цитата(Д_М @ 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.
|
|
|
|
|
Dec 9 2013, 13:14
|
Участник

Группа: Участник
Сообщений: 61
Регистрация: 22-04-06
Пользователь №: 16 379

|
Раз уж про умножение задам сюда же вопрос. микроконтроллер AT90USB128. Среда программирования JAR 4.10. Язык С++. есть необходимость помножить целое чесло на число с точкой. Задаю тип переменной double - компилятор комплирует программу, и даже результат умножения близок к истинному. Вот только сомнения меня гложут можно ли такую программу в контроллер зашивать? будет ли работать. Поэтому все переменные оставил int, но умножаю на число с точкой. Например:B = A*0.8; Пусть A = 25000 Тогда B получается 20000. Т. е. вычисляется вполне точно. Стал смотреть дизасемблер, а там какой то сложный итеративный процесс идет. Может кто нибудь на словах объяснить что контроллер делает при умножении на число с точкой?
Ну и еще вопрос по умножению целого на целое. скажем 0A0Bh x 000Ch = 7884h. При этом дизасемблер показывает, что умножение происходит по такому алгоритму:
(00x0B)+(0Cx0A) - это младший байт (0Сx0B) - старший байт результата умножения.
Может кто объяснить откуда такой алгоритм взялся?
Сообщение отредактировал mmor - Dec 9 2013, 13:20
|
|
|
|
|
Dec 9 2013, 13:37
|

Гуру
     
Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237

|
Цитата(mmor @ Dec 9 2013, 17:14)  микроконтроллер AT90USB128. Среда программирования JAR 4.10. Язык С++. есть необходимость помножить целое чесло на число с точкой. Задаю тип переменной double - компилятор комплирует программу, и даже результат умножения близок к истинному. Вот только сомнения меня гложут можно ли такую программу в контроллер зашивать? будет ли работать. ... Стал смотреть дизасемблер, а там какой то сложный итеративный процесс идет. Может кто нибудь на словах объяснить что контроллер делает при умножении на число с точкой? У микроконтроллера AT90USB128 (как впрочем и у остальных МК с архитектурой AVR) отсутствует аппаратное умножение с плавающей точкой, а потому такие операции производятся библиотекой эмуляции. Очень медленно и кода тоже много. Для большинства дел, связанных со шкалированием (умножения целых чисел на постоянный нецелый коэффициент) бывает достаточно заменить float-множитель на целочисленную дробь. Например, вместо умножения на 0.8 умножать на 4, потом делить на 5. Но если медленное умножение вас не тяготит, то можете смело довериться компилятору - тут он не ошибется, т.к. не сам сочиняет алгоритм, а пользуется библиотечным, который давным давно отлажен.
|
|
|
|
|
Dec 9 2013, 17:30
|
Участник

Группа: Участник
Сообщений: 61
Регистрация: 22-04-06
Пользователь №: 16 379

|
а насчет целочисленного умножения что нибудь подскажете?
|
|
|
|
|
Dec 10 2013, 00:05
|

Гуру
     
Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237

|
Цитата(Д_М @ 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 для бинарных операций. Однако для деления таких функций нет.
|
|
|
|
|
Dec 10 2013, 07:24
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(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 Никакого мошенничества.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Dec 10 2013, 07:26
|
Участник

Группа: Участник
Сообщений: 61
Регистрация: 22-04-06
Пользователь №: 16 379

|
да, напутал. уже разобрался в принципе умножения. как всегда все весьма просто)
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|