|
Применение целочисленной арифметики |
|
|
|
Feb 8 2007, 00:51
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(umup @ Feb 7 2007, 20:25)  Яrik Вот сказали бы это сразу и не было бы столько споров, а то уже начали приводить примеры которые на контроллерах почти не встречаются (типа 0.000076538435*25484135е3), в большинстве случаев достаточно 16 или 32 битных целых с фиксированной точкой. Для вашего случая достаточно 2х сдвигов вправо (*256 - это сдвиг влево на 8 разрядов, /1023 - почти сдвиг вправо на 10 разрядов, итого на 2 вправо), и не нужно никаких делений/умножений. Точку между цифрами, надеюсь, сами нарисуете ? А для перевода чисел в строку есть стандартная функция кажется itoa() Для таких случаев бывает формула не очень подходит. Используется бывает коррекция. Если наблюдается нелинейность. Тогда табличным методом. Если хотите всётаки полностью по формуле, то лучше целочисленным методом. Умножение на 256 Вам сказали как (сдвигов конечно не надо, просто результат читаете в байт 1 и 2 (а байт 0 - обнуляете) а деление к примеру так Код ;======================================================================== ; Поделить 32 бита (ddt:ddl) на 32 бита (dv3:dv0). ;------------------------------------------------------------------------ ; Входные регистры: ddt:ddl,dv3:dv0 ; Выходные регистры: ddt:ddl(частное), w3:w2:wh:wl(остаток) ; Портятся регистры: tmp
div32: clrq w ; очистить остаток и перенос ldi tmp,33 ; инициализировать счетчик цикла du_1: rolq dd ; делимое/результат сдвинуть влево dec tmp ; умньшить на единицу счетчик цикла brne du_2 ; переход, если не ноль ret ; выйти du_2: rolq w ; остаток сдвинуть влево subq w,dv ; остаток= остаток - делитель brcc du_3 ; если результат < 0 addq w,dv ; восстановить остаток clc ; сбросить перенос для формирования результата rjmp du_1 ; иначе du_3: sec ; установить перенос для формирования результата rjmp du_1 ; вернуться назад
где
;**************************************************************** ;* Команда SUB для 32-ух битных чисел в регистрах. * ;****************************************************************
.macro subq ; Два параметра "Имя регистров"
sub @0l,@1l ; загрузить регистр 0 sbc @0h,@1h ; загрузить регистр 1 sbc @0d,@1d ; загрузить регистр 2 sbc @0t,@1t ; загрузить регистр 3 .endm
;**************************************************************** ;* Команда ADD для 32-ух битных чисел в регистрах. * ;****************************************************************
.macro addq ; Два параметра "Имя регистров"
add @0l,@1l ; загрузить регистр 0 adc @0h,@1h ; загрузить регистр 1 adc @0d,@1d ; загрузить регистр 2 adc @0t,@1t ; загрузить регистр 3 .endm
;**************************************************************** ;* Команда CLR для 32-ух битных чисел в регистрах. * ;****************************************************************
.macro clrq ; один параметр "Имя регистров"
clr @0l ; загрузить регистр 0 clr @0h ; загрузить регистр 1 clr @0d ; загрузить регистр 2 clr @0t ; загрузить регистр 3 .endm
;**************************************************************** ;* Команда ROL для 32-ух битных чисел в регистрах. * ;****************************************************************
.macro rolq ; один параметр "Имя регистров"
rol @0l ; загрузить регистр 0 rol @0h ; загрузить регистр 1 rol @0d ; загрузить регистр 2 rol @0t ; загрузить регистр 3 .endm
;**************************************************************** ;* Команда LD для 32-ух битных чисел в регистрах. * ;****************************************************************
.macro ldq ; Два параметра "Имя регистров" и "параметр".
ld @0l,@1 ; загрузить регистр 0 ld @0h,@1 ; загрузить регистр 1 ld @0d,@1 ; загрузить регистр 2 ld @0t,@1 ; загрузить регистр 3 .endm
;**************************************************************** ;* Команда ST для 32-ух битных чисел в регистрах. * ;****************************************************************
.macro stq ; Два параметра "Имя регистров" и "параметр".
st @0,@1l ; загрузить регистр 0 st @0,@1h ; загрузить регистр 1 st @0,@1d ; загрузить регистр 2 st @0,@1t ; загрузить регистр 3 .endm
|
|
|
|
|
Feb 8 2007, 11:27
|
Частый гость
 
Группа: Новичок
Сообщений: 110
Регистрация: 8-01-07
Из: Украина
Пользователь №: 24 216

|
Цитата Для таких случаев бывает формула не очень подходит. Используется бывает коррекция. Если наблюдается нелинейность. Тогда табличным методом.
Если хотите всётаки полностью по формуле, то лучше целочисленным методом. Умножение на 256 Вам сказали как (сдвигов конечно не надо, просто результат читаете в байт 1 и 2 (а байт 0 - обнуляете) а деление к примеру так Спасибо! А нет ли у Вас примера на СИ, потму что попытка сделать асеммблерную вставку в CodeVisionAvr у меня не увенчалась успехом.
|
|
|
|
|
Feb 8 2007, 11:49
|
Знающий
   
Группа: Свой
Сообщений: 709
Регистрация: 3-05-05
Пользователь №: 4 693

|
Цитата(umup @ Feb 7 2007, 19:25)  Яrik Вот сказали бы это сразу и не было бы столько споров, а то уже начали приводить примеры которые на контроллерах почти не встречаются (типа 0.000076538435*25484135е3), в большинстве случаев достаточно 16 или 32 битных целых с фиксированной точкой. Вот в том-то и дело, что в ряде случаев, плывучка с 8-16 битной мантиссой и 8-бит экспонентой уделает и по скорости, и по динамике 32 битную целочисленку. Повторюсь. Это актуально для 8-битников. И чем горбатее 8-битник в плане арифметики, тем актуальнее. Для АРМа, например, смысла в плывучке реально нет вообще. А что встречается, а что почти не встречается, так то смотря какие задачи кем решаются. Кое кто из плывучки мож и не вылазит.
|
|
|
|
|
Feb 8 2007, 19:04
|

Ambidexter
    
Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282

|
To SasaVitebsk. I've rearranged your code a bit. It looks better and quicker now. Sorry for english text, this bl@$dy hell keyboard..
Код ;======================================== ; Поделить 32 бита (ddt:ddl) на 32 бита (dv3:dv0). ;------------------------------------------------------------------------ ; Входные регистры: ddt:ddl,dv3:dv0 ; Выходные регистры: ddt:ddl(частное), w3:w2:wh:wl(остаток) ; Портятся регистры: tmp
div32: clrq w ; очистить остаток и перенос ldi tmp,32 ; инициализировать счетчик цикла du1: subq w,dv ; остаток= остаток - делитель brcc du2 ; если результат < 0 addq w,dv ; восстановить остаток du2: rolq dd ; делимое/результат сдвинуть влево rolq w ; остаток сдвинуть влево dec tmp ; умeньшить на единицу счетчик цикла brne du1 ; переход, если не ноль comq dd ; invert the quotient ret ; выйти
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
|
Feb 8 2007, 22:16
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Яrik @ Feb 6 2007, 21:25)  Да, да какраз это... Я думал, что используя целочисленную арифметику удастся уменьшить объем занимаемой програмой. Но теперь понял, что ошибался. Я питался выводить на LCD результат измереного АЦП напряжения. В результате использования команды printf, объем памяти занимаемой программой сотавил 50% имеющейся в ATmega8. Может кто поделится своими наработками? Зарание благодарю. Дык это не плавучая арифметика столько памяти жрет, а сама функция printf!  Уже неоднократно и многими подчеркивался тот факт, что printf и sprintf требуют для работы от сотни байт до 1,5 килоБайт стековой памяти, то бишь ОЗУ. Так что советую оставить плавучку в покое, а лишь написать собственную функцию перевода числа в символьное представление. Когда-то делал устройство, поддерживающее пакетную связь по RS-485, обработку клавиатуры и вывод на два четырехсимвольных семисегментника двух дробных чисел. Также долго мучал printf, пока не понял, что на имеющихся 256 байтах ОЗУ нельзя ее пользовать. Для вывода даже одного числа с фиксированной точкой и двумя знаками после запятой printf использовал больше 100 байт стека. А в распоряжении было всего около 50 байт  В результата написал свою функцию типа поразрядного деления на степень 10, которая использовала не более 8 байт на стеке. За скоростью вычислений в таких случаях гнаться не нужно. Потому что обновлять информацию на индикаторе чаще 3 раз в секунду нет никакого смысла. Во-первых, человеческий глаз инертен. Во-вторых, наблюдателю нужно еще время чтобы осознать наблюдаемое число. Так что, используйте на здоровье плавучку и не используйте printf.
|
|
|
|
|
Feb 8 2007, 23:01
|
Знающий
   
Группа: Свой
Сообщений: 709
Регистрация: 3-05-05
Пользователь №: 4 693

|
Цитата(AndreyKeil @ Feb 8 2007, 21:27)  Извиняюсь, если мой вопрос не в тему, скажите алгоритм преобразования целого числа в формат плавающей точки и наоборот - плавающего в целое? Будете смеяться, но тупо до безобразия. Не буду рассказывать про форматы ИЕЕЕ. В ембеддед приложениях смысла в ихней упаковке нет никакого. Если што, сами найдёте. А общий смысл такой: целое число нужно привести к формату 1,хх...хх. Это называецца мантисса. Что соответствует диапазону 111...11 до 100..00. Или, в десятичном эквиваленте, от 1,9..99 до 1. И в комплект дать експоненту, т.е. тупой множитель вида 2^N, где N, я, для удобства, принимаю как ±0х3f. Ехп содержится в отдельном байте. Старший бит которого является знаком числа в целом. Таким образом плавучее число прецтавляет из себя такую конструкцию: Мант*(2^Ехп). Нуль, когда в мантиссе нуль. Это ессно. Пример. Вначале имеете число, пусть 8р. И ему в соотв. ставим експоненту. Вначале=7. Бо это номер старшево бита. Далее, сдвигаем влево и соотвецтвенно декрементируем ехпоненту. До тех пор, пока старший разряд не станет =1. Или не кончится ехпонента. Если подвинули 7 раз, а 1 в старшем разряде нет, тоды имеем нуль. Судьба. Пример 0х80 -> 0х80 мантисса и 7-експонента. Проверяем: 0х80 это 1,0. Множим его на 2^7, получаем 128. Или 0х80 целочисленно. 0х01-> 0х80мантисса и 0-експонента. Проверяем:0х80 это1,0. Множим его на 2^0, получаем 1. Оба-на, совпадло! Плывучее в целое - строго наоборот. Сдвигаем на число разрядов в ехп, предварительно отняв от неё номер старшево бита мантиссы. Если ехп получилась отриццательная, то вправо. Если положительная, то влево. Т.е. результат может быть сильно больше исходной мантиссы! Или наоборот, 0. Пример. М=0х80, Е=0х7. Тогда отымем от Е 0х7. Получим нуль. Сдвинем 0х80 на нуль влево, получим 0х80. Опять совпадло! Чюдо! Пример нумер2: М=0х80, Е=0х0. Отымаем от Е 0х7. Получаем Е=-7. Помним, что старший бит ехп - знак числа в целом. Для удобства его упаковывают в Е на время хранения. Но в работе он должен храниться отдельно для упрощения арифметики. Но отсюда вытекает проблема: в результате любых манипуляций Е должна находиться в пределах ±0х3F, или 0x3f...0xc0. Если вылетает за, значить у нас произошло исключение - результат стал равен ±бесконечность или ± не-число. Что тут делать - думайте сами. Итак, число 0х80 сдвигаем на -7 разрядов влево, т.е. вправо. Получаем 0х1. Совпало. Кто бы сомневался.
|
|
|
|
|
Feb 10 2007, 00:15
|
Частый гость
 
Группа: Новичок
Сообщений: 110
Регистрация: 8-01-07
Из: Украина
Пользователь №: 24 216

|
Цитата Дык это не плавучая арифметика столько памяти жрет, а сама функция printf! Уже неоднократно и многими подчеркивался тот факт, что printf и sprintf требуют для работы от сотни байт до 1,5 килоБайт стековой памяти, то бишь ОЗУ. Так что советую оставить плавучку в покое, а лишь написать собственную функцию перевода числа в символьное представление. Когда-то делал устройство, поддерживающее пакетную связь по RS-485, обработку клавиатуры и вывод на два четырехсимвольных семисегментника двух дробных чисел. Также долго мучал printf, пока не понял, что на имеющихся 256 байтах ОЗУ нельзя ее пользовать. Для вывода даже одного числа с фиксированной точкой и двумя знаками после запятой printf использовал больше 100 байт стека. А в распоряжении было всего около 50 байт В результата написал свою функцию типа поразрядного деления на степень 10, которая использовала не более 8 байт на стеке. За скоростью вычислений в таких случаях гнаться не нужно. Потому что обновлять информацию на индикаторе чаще 3 раз в секунду нет никакого смысла. Во-первых, человеческий глаз инертен. Во-вторых, наблюдателю нужно еще время чтобы осознать наблюдаемое число. Так что, используйте на здоровье плавучку и не используйте printf. Понял. А не могли бы Вы привести пример или алгоритм собственно самого переобразования в текст? Я как то уже спрашивал на форуме по этому поводу, но неполучил врозумительного ответа. Спасибо.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|