реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> unsigned long поделить на 10 и на 100, ну как нить совсем не стандартно...
singlskv
сообщение Apr 25 2011, 22:14
Сообщение #1


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Собственно сабжект
умножение на 429496730 и на 42949673 не предлагать,
не катит из-за нехватки регистров на нормальную реализацию... и память тю-тю...
Go to the top of the page
 
+Quote Post
rezident
сообщение Apr 25 2011, 23:43
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882



Циклическим вычитанием десятков и сотен.
На всякий случай поясняю.
Нужно найти значение X/10.
Пусть X/10=Y
X-X/10*10=0 тождество верное, не так ли?
Тогда X-Y*10=0
Вычитаем в цикле число 10 из значения X до тех пор, пока разность не будет меньше или равно нулю десяти. Считаем количество вычитаний. Значение счетчика вычитаний это Y.
Возвращаясь назад Y=X/10, что и требовалось получить.

Update. Исправил алгоритмическую ошибку в описании. Вычитать нужно пока остаток разности больше основания (10, 100 ...), а не нуля.

Сообщение отредактировал rezident - Apr 26 2011, 12:04
Go to the top of the page
 
+Quote Post
Tiro
сообщение Apr 26 2011, 05:31
Сообщение #3


Знающий
****

Группа: Свой
Сообщений: 781
Регистрация: 3-10-04
Из: Санкт-Петербург
Пользователь №: 768



Лучше через вычитания деление выполнять не последовательным вычитанием делителя, а вычитанием убывающих степеней делителя.

Например, y=x/10^c (где с = 1, 2, 3, ..)
Представим x=x9*10^9+x8*10^8+..x1*10+x0, тогда y=y9*10^(9-c)+y8*10^(8-1)+..y1*10^(1-c)+y0*10^(-c)
Естественно, все, что меньше единицы в частном нам не нужно, то есть вычисления с y0 откидываем для 10, y0 и y1 откидываем для 100. И т.д.
Собственно, алгоритм будет лишен делений и умножений, будут использоваться только вычитания и сложения.

Делим на 10:
while (x-10^9 > 0) y+=10^8 // ищем y9
while (x-10^8 > 0) y+=10^7 // ищем y8
..
while (x-10 > 0) y+=1 // ищем y1
Готово.

З.Ы. Основанием степени не обязательно должно быть 10 )))
З.З.Ы В тяжелых случаях можно делить приблизительно, умножая на дробь со знаменателем в виде 2^n, близкую к 1/10 (например 13/128)
Go to the top of the page
 
+Quote Post
_dem
сообщение Apr 26 2011, 05:55
Сообщение #4


Местный
***

Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970



BCD ?
Go to the top of the page
 
+Quote Post
hd44780
сообщение Apr 26 2011, 06:55
Сообщение #5


Профессионал
*****

Группа: Свой
Сообщений: 1 202
Регистрация: 26-08-05
Из: Донецк, ДНР
Пользователь №: 7 980



А на чем пишем? На С, на асме?

Еще один способ, может пригодится.... Не деление в прямом смысле слова, но результат дает.

Ковертируем делимое в строку: 2949673 -> "2949673". Наверняка такая функция уже есть готовая..
Для деления на 10 достаточно "откусить" от строки один знак справа, получим "294967".
На 100 - откусываем 2 знака - "29496".
Конвертируем полученную строку в число - тоже стандартная и не слишком затратная процедура.
Для оптимизации откусывание можно объединить с конвертированием. Оно ведь всегда справа начинается.

Родственные алгоритмы применяются в кассовых аппаратах, т.к. там никто никогда плавающую точку и прочие "долгоиграющие" алгоритмы не реализовывает. И все основано на подобных "хитростях".
По сути - двигаем десятичную точку вправо или влево.

Сообщение отредактировал hd44780 - Apr 26 2011, 07:01


--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса.
(с) Мария Эдуарда
Go to the top of the page
 
+Quote Post
Палыч
сообщение Apr 26 2011, 07:01
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(hd44780 @ Apr 26 2011, 10:55) *
Ковертируем делимое в строку: 2949673 -> "2949673". Наверняка такая функция уже есть готовая..
Обычно, такие функции требуют значительных ресурсов... Лучше тогда - BCD, как предложил _dem
Go to the top of the page
 
+Quote Post
=GM=
сообщение Apr 26 2011, 08:53
Сообщение #7


Ambidexter
*****

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



Цитата(singlskv @ Apr 25 2011, 21:14) *
не катит из-за нехватки регистров на нормальную реализацию... и память тю-тю

Сколько регистров есть в наличии? Если делитель состоит из одного байта, то должно хватить трёх регистров. Если к тому же делитель константа, то хватит двух регистров.


--------------------
Делай сразу хорошо, плохо само получится
Go to the top of the page
 
+Quote Post
singlskv
сообщение Apr 26 2011, 11:14
Сообщение #8


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(=GM= @ Apr 26 2011, 12:53) *
Сколько регистров есть в наличии? Если делитель состоит из одного байта, то должно хватить трёх регистров. Если к тому же делитель константа, то хватит двух регистров.

для деления unsigned long на 10 или 100 ?

суть здесь вот в чем есть стандартное деление на С которое
получает на вход 2 четырех байтовых числа и возвращает назад 4x байтовое
внутри функции деления используется еще 5 рабочих регистров
все бы хорошо, но эта функция работает не очень быстро,
ну примерно тактов 600

мне же нужно поделить только на 2 однобайтовые константы 10 и 100
стандартный в таких случаях путь домножения на 2^N / 10 не очень подходит
т.к. это предполагает умножение 2х четырех байтовых с получением восмибайтового и взятием от него старшей части
но IAR такое умножение выносит в библиотечную функцию как умножение 8байт на 8байт
и вот тут никаких регистров уже не хватает

собственно и хочется каких-нить идей как это сделать используя не больше регистров
чем использует стандартное деление и при этом что бы работало быстрее чем стандартное деление
и не использовать память

и еще что бы это было на С ...
Go to the top of the page
 
+Quote Post
hd44780
сообщение Apr 26 2011, 11:49
Сообщение #9


Профессионал
*****

Группа: Свой
Сообщений: 1 202
Регистрация: 26-08-05
Из: Донецк, ДНР
Пользователь №: 7 980



Цитата(singlskv @ Apr 26 2011, 14:14) *
и еще что бы это было на С ...


По-моему ты хочешь невозможного.
Компилятор С использует свои готовые функции для этого. Причем у каждого компилятора свои.
И он не позволит тебе их менять.
Если они тебя не устраивают, пиши свои на асме(!) и вызывай их как обычные функции.
Но будет ли это эффективнее и оптимальнее родных - фиг знает.

Но надо сперва найти какой-то алгоритм самого деления. А эти алгортимы, если мне память не изменяет, стандартны.
Например, - http://www.distedu.ru/mirror/_inform/dmivi...inform/div.html
Дальше вопрос уже в реализации этих алгоритмов.

Но ты делишь на 10 и 100, а это не степени двойки. Значит, довольствоваться общим алгоритмом.

Сообщение отредактировал hd44780 - Apr 26 2011, 11:54


--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса.
(с) Мария Эдуарда
Go to the top of the page
 
+Quote Post
ukpyr
сообщение Apr 26 2011, 12:07
Сообщение #10


Профессионал
*****

Группа: Участник
Сообщений: 1 264
Регистрация: 17-06-08
Из: бандустан
Пользователь №: 38 347



хм, почешу-ка репу...

n = n1 * 65536 + n2; // представляем как 2 16-битных
n/10 = (n1 * 65536)/10 + (n2 / 10) = (n1 * 32768)/5 + (n2 >> 1)/5 = (n1 / 5) << 15 + (n2 >> 1)/5

деление на 5 16-битного целого можно сделать так: (((U32)data * 0x3333) + 0x3333) >> 16
Go to the top of the page
 
+Quote Post
singlskv
сообщение Apr 26 2011, 12:16
Сообщение #11


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(ukpyr @ Apr 26 2011, 16:07) *
хм, почешу-ка репу...

n = n1 * 65536 + n2; // представляем как 2 16-битных
n/10 = (n1 * 65536)/10 + (n2 / 10) = (n1 * 32768)/5 + (n2 >> 1)/5 = (n1 / 5) << 15 + (n2 >> 1)/5

деление на 5 16-битного целого можно сделать так: (((U32)data * 0x3333) + 0x3333) >> 16

У меня тоже вокруг такого варианта мысли крутятся, но здесь есть ошибка
остаток от деления n1/5 сдвинутый на 15 разрядов нужно приплюсовывать к (n2>>1) и только потом делить на 5
Go to the top of the page
 
+Quote Post
=GM=
сообщение Apr 26 2011, 12:18
Сообщение #12


Ambidexter
*****

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



Цитата(singlskv @ Apr 26 2011, 10:14) *
для деления unsigned long на 10 или 100?

Если надо делить на любую константу меньшую 256, то достаточно двух регистров, если писать на ассемблере. Скорее всего, можно и на си, просто не знаю, насколько экономно иар распоряжается регистрами.


--------------------
Делай сразу хорошо, плохо само получится
Go to the top of the page
 
+Quote Post
singlskv
сообщение Apr 26 2011, 13:30
Сообщение #13


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(ukpyr @ Apr 26 2011, 16:07) *
n = n1 * 65536 + n2; // представляем как 2 16-битных
n/10 = (n1 * 65536)/10 + (n2 / 10) = (n1 * 32768)/5 + (n2 >> 1)/5 = (n1 / 5) << 15 + (n2 >> 1)/5

деление на 5 16-битного целого можно сделать так: (((U32)data * 0x3333) + 0x3333) >> 16

Спасибо за гениальную sm.gif идею поделить сначала на 2 sm.gif а уже потом на 5,
а то у меня никак разрядности не хватало.
Если нигде не ошибся то код для деления на 10 такой:
Код
              DWORD N = ...; // делимое
              DWORD result;
              N >>= 1;
              result = (((N >> 16) + 1) * 0x3333) & 0xFFFF0000;
              result |= ((N - result * 5 + 1) * 0x3333) >> 16;


итого примерно 100 тактов против 600 у обычного деления.

Теперь нужно /100 , хотя конечно можно 2 раза поделить на 5...
Go to the top of the page
 
+Quote Post
ukpyr
сообщение Apr 26 2011, 14:28
Сообщение #14


Профессионал
*****

Группа: Участник
Сообщений: 1 264
Регистрация: 17-06-08
Из: бандустан
Пользователь №: 38 347



желательно протестировать весь диапазон long int, мало ли что...

протестировал - ваш алгоритм дает кучу ошибок, начиная с 131080 (вместо 13108 получается 13107).
там после умножения на 0x3333 обязательно нужна коррекция - прибавить 0x3333

Сообщение отредактировал ukpyr - Apr 26 2011, 14:41
Go to the top of the page
 
+Quote Post
singlskv
сообщение Apr 26 2011, 15:19
Сообщение #15


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(ukpyr @ Apr 26 2011, 18:28) *
желательно протестировать весь диапазон long int, мало ли что...

протестировал - ваш алгоритм дает кучу ошибок, начиная с 131080 (вместо 13108 получается 13107).
там после умножения на 0x3333 обязательно нужна коррекция - прибавить 0x3333

коррекция на 0x3333 уже есть в коде
но ошибки действительно есть т.к. после добавления остатка от деления старшей части
у нас диапазон становится 0 - 0x50000

сейчас будем допиливать...

Вроде так корректно:
Код
              DWORD N = ...; // делимое
              DWORD result;
              N >>= 1;
              result = (((N >> 16) + 1) * 0x3333) & 0xFFFF0000;
              N = N - result * 5 + 1;
              N += N >> 16; // коррекция для диапазона 0x10000 - 0x50000
              result |= (N  * 0x3333) >> 16;

Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 26th June 2025 - 11:47
Рейтинг@Mail.ru


Страница сгенерированна за 0.01505 секунд с 7
ELECTRONIX ©2004-2016