[P&AM Lab] Бибиотека больших чисел
Grigoriy A. Sitkarev
sitkarev на komitex.ru
Вт Фев 22 00:08:32 MSK 2011
Хорошее начало.
> Начал писать библиотеку больших чисел. Небольшие трудности возникают с
> определением того из чего она должна вообще состоять и какие функции
должны в
> ней быть. Пока написал только сложение, планируется ещё вычитание,
умножение,
> деление, остаток от деления.
Библиотека это в первую очередь API -- т.е. интерфейс вызова, имена
функций, их порядок в аргументах и возвращаемые значения. Для того чтобы
API был хорошим нужно чтобы он был:
а) целостным;
б) непротиворечивым;
в) простым для запоминания;
г) соответствовал задачам библиотеки и ожиданиям пользователя;
д) основывался на сложившихся традициях и идиомах (если таковые
применимы и существуют в практике).
Возможно, сначала стоит разобраться вообще -- зачем нужна такая
библиотека? Что мы от неё хотим и как мы будем её использовать? Для
этого нужно погрузиться на какое-то время в предметно-ориентированную
область и изучить её, хотя бы поверхностно. На этом шаге мы должны чётко
сформулировать, какие функции мы хотим получить от библиотеки. Их задача
-- сделать сложные вещи простыми и дать возможность ими воспользоваться.
Дальше разрабатывается API, т.е. интерфейс к этим функциям, их имена,
порядок следования аргументов, публичные структуры, перечисления и т.д.
Прототип будущей библиотеки. Возможно, в ходе реализации отдельных
функций выяснится что что-то нужно скорректировать на любом из шагов,
тогда задача выполняется ещё раз, от начала. Возможно, какой-то шаг
пропускается. Жёсткого алгоритма конечно-же нет, это творчество. То что
получилось на этом шаге -- выносится в заголовочный файл.
> Нужно ли писать что-то ещё? есть ли смысл вводить
> отрицательные числа? Нужно ли писать функции так, что бы они сразу могли
> складывать разные по размеру числа?
Можно сказать что такие функции точно понадобятся (и ещё будут другие):
bnum_new()
bnum_free()
bnum_unset()
bnum_reset()
bnum_add()
bnum_sub()
bnum_mul()
bnum_div()
bnum_mod()
bnum_gcd()
bnum_exp() или bnum_pow()
bnum_set()
bnum_set_int()
bnum_set_ascii()
bnum_get_ascii()
Что-то вроде... Надо подумать ещё конечно. Это очень важный шаг.
Если например, я беру два положительных числа a и b, и при этом a < b, а
потом вычитаю из (a - b), то результатом будет отрицательное число. Это
ответ на вопрос об отрицательных числах. Такая арифметика будет полной.
Для хранения знака тебе достаточно одного бита.
Базу для чисел скорее всего лучше выбрать 65536 и тогда значения
разрядов хранить в unsigned short. Числа bignum могут быть разного
порядка, поэтому складывать и делать прочие операции нужно с числами
разного размера. Поэтому придётся хранить поле где есть индекс разряда
последнего использованного. Индекс удобнее потому что в таком случае он
же будет являться и наибольшей степенью базы у числа. Например,
65535^0=1 если top равен 0.
#define BNUM_BASE 65536
struct bignum {
int sign; /* 1 = negative, 0 = positive */
int nalloc; /* allocated */
int top; /* last used */
unsigned short *val; /* BNUM_BASE digits */
};
Что-то вроде такого.
Структуры должны передаваться по указателю а не по значению. Сейчас они
у тебя копируются в стек. Должно быть что-то вроде:
/* res = a + b */
int bnum_add(struct bignum *res, struct bignum *a, struct bignum *b);
Боря, обрати внимание пожалуйста на стиль. У тебя с этим есть проблемы.
Может быть у тебя какой-то редактор неправильный, не даёт тебе
возможность писать удобно?
--
Г.А.
Подробная информация о списке рассылки Lab