|
unsigned long поделить на 10 и на 100, ну как нить совсем не стандартно... |
|
|
|
Apr 26 2011, 05:31
|
Знающий
   
Группа: Свой
Сообщений: 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)
|
|
|
|
|
Apr 26 2011, 11:14
|
дятел
    
Группа: Свой
Сообщений: 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байт и вот тут никаких регистров уже не хватает собственно и хочется каких-нить идей как это сделать используя не больше регистров чем использует стандартное деление и при этом что бы работало быстрее чем стандартное деление и не использовать память и еще что бы это было на С ...
|
|
|
|
|
Apr 26 2011, 11:49
|

Профессионал
    
Группа: Свой
Сообщений: 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
--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса. (с) Мария Эдуарда
|
|
|
|
|
Apr 26 2011, 13:30
|
дятел
    
Группа: Свой
Сообщений: 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 Спасибо за гениальную  идею поделить сначала на 2  а уже потом на 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...
|
|
|
|
|
Apr 26 2011, 15:19
|
дятел
    
Группа: Свой
Сообщений: 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;
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|