Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Применение целочисленной арифметики
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Яrik
Добрый вечер!

Для деления чисел с плавающей запятой необходимо много памяти МК. Но знаю, что эту операцию можно сделать, используя целочисленую арифметику.
Подскажите, пожалуйста, кто знает как обращаться с этим зверем. biggrin.gif
umup
очень просто - при вычислениях просто используйте целые числа, домноженные на нужное число 10ток после запятой, например для отбражения числа 1.4 с одним знаком после запятой получится 14, с двумя - 140. Ясно что при умножениях/делениях и других операциях это нужно учитывать, например 1.4 * 1.5 = 2.1, а 14 * 15 = 210, то есть после умножения нужно еще разделить на 10 чтобы получить 21
mihask
Цитата(Яrik @ Feb 6 2007, 03:15) *
Добрый вечер!

Для деления чисел с плавающей запятой необходимо много памяти МК. Но знаю, что эту операцию можно сделать, используя целочисленую арифметику.
Подскажите, пожалуйста, кто знает как обращаться с этим зверем. biggrin.gif


Что-то мне кажется что одним делением не обойдется smile.gif, я конечно не знаю условий вашей задачи , но скорей всего придется всю математику или ,по крайней мере, наиболее ресурсоемкую часть перевести
на целочисленную арифметику, а то можно много потерять на преобразовании переменных из одного формата в другой. От себя добавлю пример со сложением smile.gif
1 + 0.35 =1.35 в целочисленном формате 100 + 35 =135 - подразумеваем что у нас два знака после
запятой. Деление - наиболее эффективно методом сдвига , так это помоему называется smile.gif, алгоритма не
помню, могу дать исходник функции целочисленного деления на asm 51- ого процессора smile.gif.
Кстати деление в столбик, которое здесь тоже можно применить, - это очень медленный вариант.

Эффективные алгоритмы целочисленного деления описаны в книге - "Алгоритмические трюки для программистов" - в сети можно скачать точно, если не удастся найти, могу дать.
umup
еще посмотрите на http://umup.narod.ru/#books_cs_arith - есть кое-что по комп.арифметике
gormih
Цитата(mihask @ Feb 6 2007, 03:15) *
Эффективные алгоритмы целочисленного деления описаны в книге - "Алгоритмические трюки для программистов" - , если не удастся найти, могу дать.


если можно вот сюда
mse
Цитата(Яrik @ Feb 6 2007, 00:15) *
Добрый вечер!

Для деления чисел с плавающей запятой необходимо много памяти МК. Но знаю, что эту операцию можно сделать, используя целочисленую арифметику.
Подскажите, пожалуйста, кто знает как обращаться с этим зверем. biggrin.gif

Ничё не понял...чего это вдруг "много"? Плывучка, кстати, будет занимать "меньше" места и времени, по сравнению с аналогичной по динамицкому диапазону представления чисел, целочисленкой. Умножение-деление - в разы. Сложение-вычитание, разве что, проиграет. Тоже в разы. 5...8 тактов против 10-20-50.
Другое дело, если операнды реально укладываются в слово-два-три процессора. Тогда смысл есть.
SasaVitebsk
Цитата(mse @ Feb 6 2007, 11:05) *
Ничё не понял...чего это вдруг "много"? Плывучка, кстати, будет занимать "меньше" места и времени, по сравнению с аналогичной по динамицкому диапазону представления чисел, целочисленкой. Умножение-деление - в разы. Сложение-вычитание, разве что, проиграет. Тоже в разы. 5...8 тактов против 10-20-50.
Другое дело, если операнды реально укладываются в слово-два-три процессора. Тогда смысл есть.


Совершенно согласен. Я это проверял. Единственное что усложняет работу с флоат это ввод/вывод в десятичной с.с. А сама арифметика просто прекрасно считается. Тем более у AVR команды есть соответствующие. Тоже применял.

Так что всё зависит от разрядности чисел и необходимости их отображения. А на счёт памяти не парьтесь - и регистрами обойдётесь.
Яrik
Цитата
Единственное что усложняет работу с флоат это ввод/вывод в десятичной с.с


Да, да какраз это... Я думал, что используя целочисленную арифметику удастся уменьшить объем занимаемой програмой. Но теперь понял, что ошибался.
Я питался выводить на LCD результат измереного АЦП напряжения. В результате использования команды printf, объем памяти занимаемой программой сотавил 50% имеющейся в ATmega8.
Может кто поделится своими наработками? Зарание благодарю.
SasaVitebsk
Но результат АЦП - целочисленный. blink.gif

Пожалуйста, если хотите получить полный ответ, задайте полный вопрос.
mse
Цитата(Яrik @ Feb 6 2007, 19:25) *
Цитата
Единственное что усложняет работу с флоат это ввод/вывод в десятичной с.с


Да, да какраз это... Я думал, что используя целочисленную арифметику удастся уменьшить объем занимаемой програмой. Но теперь понял, что ошибался.
Я питался выводить на LCD результат измереного АЦП напряжения. В результате использования команды printf, объем памяти занимаемой программой сотавил 50% имеющейся в ATmega8.
Может кто поделится своими наработками? Зарание благодарю.

http://www.telesys.ru/wwwboards/mcontrol/2...es/141026.shtml
сразу в АSCII http://www.telesys.ru/wwwboards/mcontrol/2...es/141922.shtml
lazycamel
Цитата(mse @ Feb 6 2007, 11:05) *
Плывучка, кстати, будет занимать "меньше" места и времени, по сравнению с аналогичной по динамицкому диапазону представления чисел, целочисленкой.


Обычный пример для плавучки - ATAN(ADC0/ADC1)*PI
ИМХО целочиcленка и CORDIC будут не просто в разы, а на порядок быстрее.
umup
я пользуюсь этим :

http://electronix.ru/forum/index.php?showt...mp;#entry188097

ilcd_ks066_pul(data, buf, options) - вывод беззнакового long с фиксированной точкой в память
ilcd_ks066_psl(data, buf, options) - вывод знакового long

а из памяти - на LCD
mse
Цитата(lazycamel @ Feb 7 2007, 17:02) *
Обычный пример для плавучки - ATAN(ADC0/ADC1)*PI
ИМХО целочиcленка и CORDIC будут не просто в разы, а на порядок быстрее.

Обычный пример для плывучки: 0.000076538435*25484135е3
Без ИМХОв: плывучка оденет целочисленку, пусть не на порядок, но в разы.
На порядок она оденет при делении лонг-лонг на его-же.
И кто мешает реализовать ATAN() таблично? КОРДИК тихо удавится в сортире. Без разницы, целочисленно или вплавь. Тем более, непонятно, кто мне запретит реализовать его вплавь?
Не надо путать алгоритм с операндом.
Яrik
Цитата
Пожалуйста, если хотите получить полный ответ, задайте полный вопрос.

Да, результат преобразования целое число (от 0 до 1023), но на LCD мне нужно вывести выраженый в вольтах. Для перевода внутреним опорним напряжением, я пользуюсь следующей формулой:Нажмите для просмотра прикрепленного файла
umup
Яrik
Вот сказали бы это сразу и не было бы столько споров, а то уже начали приводить примеры которые на контроллерах почти не встречаются (типа 0.000076538435*25484135е3), в большинстве случаев достаточно 16 или 32 битных целых с фиксированной точкой.
Для вашего случая достаточно 2х сдвигов вправо (*256 - это сдвиг влево на 8 разрядов, /1023 - почти сдвиг вправо на 10 разрядов, итого на 2 вправо), и не нужно никаких делений/умножений. Точку между цифрами, надеюсь, сами нарисуете ?
А для перевода чисел в строку есть стандартная функция кажется itoa()
SasaVitebsk
Цитата(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
Яrik
Цитата
Для таких случаев бывает формула не очень подходит. Используется бывает коррекция. Если наблюдается нелинейность. Тогда табличным методом.

Если хотите всётаки полностью по формуле, то лучше целочисленным методом. Умножение на 256 Вам сказали как (сдвигов конечно не надо, просто результат читаете в байт 1 и 2 (а байт 0 - обнуляете) а деление к примеру так

Спасибо! А нет ли у Вас примера на СИ, потму что попытка сделать асеммблерную вставку в CodeVisionAvr у меня не увенчалась успехом.
mse
Цитата(umup @ Feb 7 2007, 19:25) *
Яrik
Вот сказали бы это сразу и не было бы столько споров, а то уже начали приводить примеры которые на контроллерах почти не встречаются (типа 0.000076538435*25484135е3), в большинстве случаев достаточно 16 или 32 битных целых с фиксированной точкой.

Вот в том-то и дело, что в ряде случаев, плывучка с 8-16 битной мантиссой и 8-бит экспонентой уделает и по скорости, и по динамике 32 битную целочисленку. Повторюсь. Это актуально для 8-битников. И чем горбатее 8-битник в плане арифметики, тем актуальнее. Для АРМа, например, смысла в плывучке реально нет вообще.
А что встречается, а что почти не встречается, так то смотря какие задачи кем решаются. Кое кто из плывучки мож и не вылазит.
=GM=
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                              ; выйти

AndreyKeil
Извиняюсь, если мой вопрос не в тему, скажите алгоритм преобразования целого числа в формат плавающей точки и наоборот - плавающего в целое?
SasaVitebsk
Цитата(Яrik @ Feb 8 2007, 12:27) *
Спасибо! А нет ли у Вас примера на СИ, потму что попытка сделать асеммблерную вставку в CodeVisionAvr у меня не увенчалась успехом.


Ну Вы и даёте. smile.gif А на си это выглядит в точности как Вы написали.

Res=adc_res*256/1023;
rezident
Цитата(Яrik @ Feb 6 2007, 21:25) *
Да, да какраз это... Я думал, что используя целочисленную арифметику удастся уменьшить объем занимаемой програмой. Но теперь понял, что ошибался.
Я питался выводить на LCD результат измереного АЦП напряжения. В результате использования команды printf, объем памяти занимаемой программой сотавил 50% имеющейся в ATmega8.
Может кто поделится своими наработками? Зарание благодарю.

Дык это не плавучая арифметика столько памяти жрет, а сама функция printf! excl.gif Уже неоднократно и многими подчеркивался тот факт, что printf и sprintf требуют для работы от сотни байт до 1,5 килоБайт стековой памяти, то бишь ОЗУ. Так что советую оставить плавучку в покое, а лишь написать собственную функцию перевода числа в символьное представление.
Когда-то делал устройство, поддерживающее пакетную связь по RS-485, обработку клавиатуры и вывод на два четырехсимвольных семисегментника двух дробных чисел. Также долго мучал printf, пока не понял, что на имеющихся 256 байтах ОЗУ нельзя ее пользовать. Для вывода даже одного числа с фиксированной точкой и двумя знаками после запятой printf использовал больше 100 байт стека. А в распоряжении было всего около 50 байт smile.gif В результата написал свою функцию типа поразрядного деления на степень 10, которая использовала не более 8 байт на стеке. За скоростью вычислений в таких случаях гнаться не нужно. Потому что обновлять информацию на индикаторе чаще 3 раз в секунду нет никакого смысла. Во-первых, человеческий глаз инертен. Во-вторых, наблюдателю нужно еще время чтобы осознать наблюдаемое число. Так что, используйте на здоровье плавучку и не используйте printf. wink.gif
mse
Цитата(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. Совпало. Кто бы сомневался.
add
mse, немогли бы Вы пояснитьследующее, если к примеру взять число 56 (0b00111000) после двух смещений влево получили 224 (0b11100000) и E=5. Это как считать? 1,96*2^5 в десятичке? несовсем понятно?!
заранее пасиба за ответ.
mse
Цитата(add @ Feb 9 2007, 12:48) *
mse, немогли бы Вы пояснитьследующее, если к примеру взять число 56 (0b00111000) после двух смещений влево получили 224 (0b11100000) и E=5. Это как считать? 1,96*2^5 в десятичке? несовсем понятно?!
заранее пасиба за ответ.

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


Понял. А не могли бы Вы привести пример или алгоритм собственно самого переобразования в текст? Я как то уже спрашивал на форуме по этому поводу, но неполучил врозумительного ответа. Спасибо.
rezident
Цитата(Яrik @ Feb 10 2007, 02:15) *
Понял. А не могли бы Вы привести пример или алгоритм собственно самого переобразования в текст? Я как то уже спрашивал на форуме по этому поводу, но неполучил врозумительного ответа. Спасибо.

Вот из "пустыни" целая ветка примерно на эту же тему http://caxapa.ru/79812.html?todo=full
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.