Страница 1 из 6

Правильное выполнение арифметических операций?

Добавлено: Вт мар 22, 2022 21:06:56
WatchCat
Программа получает два числа которые являются результатами работы АЦП. Это напряжение и ток.
Надо вычислить сопротивление. Соответственно поделить одно на другое.

С АЦП приходит что-то типа такого
23906 и 4320
ну или в шестандцатиричном виде
0x5d62 и 0x10e0

Если я просто поделю одно на другое целочисленным делением (на Си) то получу число 5, что означает весьма существенную потерю
точности из-за малого числа значащих разрядов. В действительности-то там 5.533796
Возникает мысль домножить первое число на 1000 перед операцией деления. Тогда деление даст 0х159d или в десятичном виде 5533,что в смысле точности уже вполне устраивает,а запятую можно и "подразумевать",главное что значащие разряды сохранены.
Но такой финт ушами требует 32-разрядного целого числа чтобы результат предварительного умножения туда влез. А это на AVR может быть не быстро(не тестировал еще,просто предполагаю).
Собственно и вопрос к присутствующим - Какие еще есть варианты решения этой задачки?
Знаю что программистов в институте учат всякой хитрой двоичной арифметике,но сам я учился на электронщика,да и то в те времена когда компьютеры были большими:-) Так что если кто в этой теме хорошо разбирается - подскажите пожалуйста!
Ну или порекомендуйте где почитать или какие слова спросить у гугла.

Re: Правильное выполнение арифметических операций?

Добавлено: Вт мар 22, 2022 23:03:10
AlexS4
скорее всего у вашей avr нет никакой нинструкции деления
Спойлервот полный список мультипликативных операций средней меги:
MUL Rd, Rr Multiply unsigned R1:R0 ← Rd x Rr Z, C 2
MULS Rd, Rr Multiply signed R1:R0 ← Rd x Rr Z, C 2
MULSU Rd, Rr Multiply signed with unsigned R1:R0 ← Rd x Rr Z, C 2
FMUL Rd, Rr Fractional multiply unsigned R1:R0 ← (Rd x Rr) << Z, C 2
FMULS Rd, Rr Fractional multiply signed Z, C 2
FMULSU Rd, Rr Fractional multiply signed with unsigned 1
R1:R0 ← (Rd x Rr) << 1
R1:R0 ← (Rd x Rr) << 1 Z, C 2
, а сишный / все равно реализован множеством команд, так что беспокоиться о скорости думаю ненужно ;)

A. если компилятор умеет операции с 32bit - просто загрузите такие переменные (double unsigned int напр) и умножте как хотели.

если нет то сходу простейшие варианты такие:

B. если весь диапазон чисел близок к тем что в примере
то можно сделать статическое предварительное преобразование аргументов:
напр умножить на 2 числитель (одна инструкция ror) и тогда он поместится в 16bit unsigned если будет <32,7k
и нацело поделить знаменатель либо на 50 если важны десятичные значения сдвига "запятой", или на степень 2, если борьба за каждый такт по скорости.
и тогда целый результат будет умноженным на 100 и примерно 5-6потеряных бита в знаменателе.

если такие потери неприемлемы или диапазон широкий то:

C. делите нацело получив целую часть, и отдельно получайте остаток от деления (оператор % ) , остаток умножайте на 100 или скажем на 10000 и делите на знаменатель получая сотые или десятитысячные доли.

unsigned int z,p;
z=a/b; p=a%b; p*=1000;p/=b; \\ 1000a/b=1000z+p

обязательно последовательно, отдельными присваиваниями как в этом примере или с запретом арифм оптимизации, иначе компилятор может переставить последовательность операций и получится чушь.

D. если хочется всегда максимальную точность при максимальном диапазоне аргументов то надо делать С. в несколько итераций, например умножая остаток на десять, находим остаток от деления остатка , снова его на 10 итд.
(максимальная точность будет если на 2 умножать и находить очередной бит дробной части.)


если на пальцах то практичные алгоритмы умножения и деления с расширением разрядности похожи на алгоритмы ручных операций в столбик только цифры уже не 0..9 а скажем 0..255 или 0..65535 итд.

если подробнее то для самого начала:
Дональд Кнут Искусство программирования. Том 2. Получисленные алгоритмы. глава: арифметика многократной точности.

если же хочется погрузиться в тему теоретически то вот интересная статья http://www.ccas.ru/karatsuba/alg.htm

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 00:38:57
Martian
А ещё иногда можно подумать и найти нецифровое решение для частного случая, если уж так хочется скорости и/или компактности программы. Например, вспомнить про некоторые способы применения ОУ как аналоговых вычислителей. в сундуке есть книжка "Применение прецизионных аналоговых микросхем".

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 06:21:01
akl
WatchCat писал(а):...Но такой финт ушами требует 32-разрядного целого числа чтобы результат предварительного умножения туда влез. А это на AVR может быть не быстро(не тестировал еще,просто предполагаю)... Какие еще есть варианты решения этой задачки?
На асме этот пример (умножение на 100'000 с последующим делением) занимает 1754 такта. Программы умножения и деления 40 разрядные.

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 07:41:21
ARV
самое важное - не надо делать все эти 32-битные умножения-деления в обработчике прерываний, и тогда на их быстродействие будет наплевать.

Добавлено after 1 minute 21 second:
кстати, просто для справки: во всем известной ардуине практически ВСЕ библиотеки используют float для вычислений, и ничего, это никак не мешает делать на ардуинах всё, что угодно - быстродействия хватает. так что если не жалко памяти, применяйте float, и не парьтесь

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 14:10:58
Martian
Можно и в обработчике, почему нет? Просто соблюдать некоторые правила, исключающие небезопасное поведение.

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 14:20:20
WatchCat
[uquote="AlexS4",url="/forum/viewtopic.php?p=4202327#p4202327"]скорее всего у вашей avr нет никакой нинструкции деления[/uquote]
То,что у мелких контроллеров нет операции деления float для меня очевидно,но вот что нет целочисленного деления - было для меня новостью. Сам-то я только x86 ассемблер знаю,тот что во времена MS DOS использовался. - немало писал на нем по работе тридцать лет назад. С повышением производительности компов как-то отпала регулярная надобность в ассемблере. Да и компиляторы Си стали делать код нередко даже лучше чем руками напишешь.
Ну и извиняюсь что не уточнил сразу - у меня Atmega328p. Некоторое количество их досталось в виде мелких модулей примерно 2х3см размером,из имущества обанкротившегося кружка детской робототехники. Я срисовал схему с платы,благо она там простейшая,и использую модули в своих поделках,благо шаг отверстий по краям совпадает с шагом отверстий на дырчатых макетках - можно модуль впаять на "ножках" из медной проволоки.
сишный / все равно реализован множеством команд, так что беспокоиться о скорости думаю ненужно ;)
Сейчас допишу письмо,скомпилирую тестовый сишный пример в ассемблер,благо gcc это умеет,и полезу изучать как сделано.
Вот уж действительно неожиданность.
если компилятор умеет операции с 32bit - просто загрузите такие переменные (double unsigned int напр) и умножте как хотели.
Как показал эксперимент, правильно работает вот такая конструкция с 16-разрядными целыми если результат заведомо влезает в 16 разрядов,а мне как раз отсчеты с двух каналов АЦП поделить одно на другое захотелось.
a = (unsigned long)b * 1000 / c
За счет указания явного приведения типа gcc понимает как считать и выдает результат,сдвинутый на три разряда (десятичных) влево,а не 5 вместо 5.533. Есть небольшая тонкость - происходит уменно усечение результата,а не классическое округление. Кстати,округление в математическом справочнике - это две страницы текста,а не то школьное правило которое все знают.
делите нацело получив целую часть, и отдельно получайте остаток от деления (оператор % ) , остаток умножайте на 100 или скажем на 10000 и делите на знаменатель получая сотые или десятитысячные доли.
unsigned int z,p;
z=a/b; p=a%b; p*=1000;p/=b; \\ 1000a/b=1000z+p
Вот за эту безусловно красивую идею - Спасибо! И без влезания в ассемблер,на чистом Си,и точность сохраняется и без плавающих чисел. Вот что значит профи в программировании - я,самоучка, до такого бы не додумался.

[uquote="ARV"]самое важное - не надо делать все эти 32-битные умножения-деления в обработчике прерываний, и тогда на их быстродействие будет наплевать.[/uquote]
Понятно что не в прерывании. Программы для Меги я обычно делаю по такой схеме:

Код: Выделить всё

ISR() /*прерывание вызываемое по таймеру*/
{
 /*опрос датчика*/
 /*команда исполнительному механизму,например запись в регистр таймера,формирующего шим*/
}

main()
{
while(TRUE)
  {
    /*расчет нового значения команды исполнительному механизму*/
    sleep_mode();
    /*другая полезная деятельность*/
  }
}
Сама идея придумана во времена MS DOS 3.30 чтобы обеспечивать там работу фоновых процессов.
Связь между основным телом программы и обработчиком прерывания - через глобальные переменные.
Как оказалось, вызов прерывания побочным эффектом имеет выход из функции sleep_mode(),в результате контроллер прокручивает один цикл и снова усыпает до следующего прерывания. А не молотит вхолостую поедая электричество как было бы без sleep_mode()
Тем более что и смысла нет гонять расчетный цикл пока не получены новые значения с датчика.
А если прерываний несколько разных - то программа вообще становится похожа на пьесу для механического пианино:)
Выполняется только то что нужно и когда нужно - без всякого кручения холостых циклов с опросами чего-нибудь.
Благо что в Атмеге всегда можно получить прерывание от чего угодно,даже просто от дергания её за ногу.
В одной самоделке я к такой ноге подключил четыре кнопки через диодное "или". Разумеется, еще и каждую кнопку на свой отдельный пин. В результате не надо крутить постоянно цикл опроса кнопок. Любая кнопка сама сообщит когда будет нажата,и можно её обрабатывать.
Понятно что в профессиональных схемах должен быть специфический контроллер клавиатуры типа например pcf8574 - да где же я его в деревне возьму? Потому и заменил конструкцией из rc-цепочек для подавления дребезга и диодного "или".
кстати, просто для справки: во всем известной ардуине практически ВСЕ библиотеки используют float
У ардуино там специфическая "среда" для программирования и не менее специфический язык.
Еще и загрузка в контроллер через специальный бутлоадер вместо привычного программатора.
Всё это изучать надо,а мне в силу пожилого возраста уже несколько лень. Как-то за четверть века привык пользоваться обычным текстовым редактором и компилятором gcc. С gcc познакомился еще под ДОСом в 90х после того как достали кглюки компилатора MS C 5.10. Потом debian c 96 года и по сей день. С 05 года и Атмелы по той же технологии программирую. Привычка - сильная штука. Так что я думаю что доживу свой век без изучения ардуино. Тем более что уже полтора десятка лет как по компьютерной теме не работаю - чисто радиолюбитель,причем еще и сельский давно уже:)
А что касается float - добавится возня с контролем преобразовани типов. Потому как результат работы АЦП - это int, в регистр таймера ШИМ тоже пищется int. Иметь "посредине" long - еще приемлимо,а вот float совсем уже неудобно.

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 14:36:33
ARV
WatchCat писал(а):скомпилирую тестовый сишный пример в ассемблер,благо gcc это умеет,и полезу изучать как сделано.
зачем? достаточно уверенно можно заявить, что компилятор делает очень хорошо и правильно
WatchCat писал(а):Как показал эксперимент, правильно работает вот такая конструкция с 16-разрядными целыми если результат заведомо влезает в 16 разрядов
оно бы и иначе правильно работало бы: a = b * 1000UL / c
WatchCat писал(а):В результате не надо крутить постоянно цикл опроса кнопок. Любая кнопка сама сообщит когда будет нажата,и можно её обрабатывать.
а вот лично я из многих десятков своих проектов считаные единицы (пальцев одной руки хватит) раз делал кнопки на прерываниях, все остальные случаи - опрос из главного цикла там, где это и в самом деле нужно. это я просто к тому, что все полезно, что в рот полезло.
WatchCat писал(а):У ардуино там специфическая "среда" для программирования и не менее специфический язык.
ничего специфического - С++ обычный, GCC-шный.
просто библиотеки классов слегка спрятаны...
WatchCat писал(а):А что касается float - добавится возня с контролем преобразовани типов. Потому как результат работы АЦП - это int, в регистр таймера ШИМ тоже пищется int. Иметь "посредине" long - еще приемлимо,а вот float совсем уже неудобно
см. выше - удобно или не удобно, имхо, чисто человеческие понятия. если человеку удобно, то так и есть смысл делать. а за МК переживать, что ему, дескать, неудобно, не нужно совсем - он кремниевый, все стерпит.

Добавлено after 7 minutes 25 seconds:
кстати, об округлении. если вы хотите "правильно" округлять результат, то сначала прибавьте к делимому половину величины округляемого разряда, а потом делите. т.е. если надо округлить до 10, прибавьте 5, если надо округлять до 100 - прибавьте 50.

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 14:46:35
tonyk
Если бы ты использовал С++, то просто взял бы реализацию типа большого целого и выполнил все вычисления с той разрядностью, которая нужна для сохранения точности, хоть 32 бита, хоть 80, хоть 160. Я в своё время использовал класс, который умеет динамически наращивать разрядность чисел в случае переполнения, правда, я этим не пользовался, а сразу выставлял достаточную.

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 14:52:43
Dimon456
AlexS4, сравнительный тест показывает
Спойлер

Код: Выделить всё

p*=1000;
за пределами uint16_t, соответственно p уже будет uint32_t.

Код: Выделить всё

volatile uint16_t z,a,b;
volatile uint32_t  p;

	a= 23906;
	b= 4320;
	z=a/b; 
	p=a%b; 
	p*=1000;	
	p/=b;
выполнено за 1,1180ms

Код: Выделить всё

volatile float aa,bb;

	aa = 23906;
	bb = 4320;
	aa /= bb;
выполнено за 0.580us

то есть float выполняется в 2 раза быстрее.

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 15:18:55
ARV
Dimon456 писал(а):то есть float выполняется в 2 раза быстрее.
слухи о тяжеловесности и медлительности float оказались слухами? :)))

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 16:08:34
AlexS4
Dimon456, ну мы же понимаем что это только к вопросу определения оператора / для одних и других типов....
конечно заменять 1 деление даже флот на 4 мультипликативные операции это врядли напрямую повысит быстродействие,
но некий выигрыш может быть напр за счет упаковки результата, флот еще надо будет распаковать для общего сучая жеж.

смотрели код для обоих? любопытно как они добились таких успехов)))
какой gcc?

[uquote="ARV",url="/forum/viewtopic.php?p=4202386#p4202386"]кстати, просто для справки: во всем известной ардуине практически ВСЕ библиотеки используют float для вычислений, и ничего, это никак не мешает делать на ардуинах всё, что угодно - быстродействия хватает. так что если не жалко памяти, применяйте float, и не парьтесь[/uquote]
угу ;) вот поэтому ардуиньщики никогда не понимают как можно обходиться 32 БАЙТАМИ ram какойнить t10 когда им для той же задачи не хватает 8й меги :))) ну и замедление простеньких целочисленных операций тоже невсегда хорошо... кроме как для производителей чипов, которые продадут 5 кристаллов туда где справился бы 1.

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 16:54:44
WatchCat
[uquote="Dimon456",url="/forum/viewtopic.php?p=4202596#p4202596"]то есть float выполняется в 2 раза быстрее.[/spoiler][/uquote]
[/uquote]
Как это вообще может быть на процессоре,не имеющем аппаратной команды деления?
Кстати,а как/чем удалось померить время в этих тестах?
Заодно,можно ли померить вот эти варианты:
a = (unsigned long)b * 1000 / c
a = b * 1000UL / c

А еще к времени собственно вычисления в float надо бы добавить время преобразования из int в float и,что особенно беспокоит,время обратного преобразования в int,которое потенциально грозит глюками так как надо проверять значение на допустимый диапазон.
К тому же если я получу математически верный результат 5.533 в виде float то его всё равно придется потом домножать на 1000 перед преобразованием в int иначе будет сильная потеря точности. Так что к тестовому примеру с float надо добавить два преобразования типов,одну проверку на допустимость диапазона и одно умножение.

Добавлено after 3 minutes 40 seconds:
[uquote="tonyk",url="/forum/viewtopic.php?p=4202591#p4202591"]Если бы ты использовал С++[/uquote]
Непрофессионалу без многолетнего стажа программирования на плюсах - намного сложнее написать на них безошибочный код и/или найти ошибки если они всё-таки случились. В случае привычного с институтских времен обычного Си это намного проще. Гораздо меньше скрытых от программиста побочных эффектов и неочевидного поведения.

Добавлено after 3 minutes 2 seconds:
[uquote="ARV",url="/forum/viewtopic.php?p=4202586#p4202586"]см. выше - удобно или не удобно, имхо, чисто человеческие понятия. если человеку удобно, то так и есть смысл делать. а за МК переживать, что ему, дескать, неудобно, не нужно совсем - он кремниевый, все стерпит.[/uquote]
Понятно что я о человеческом удобстве говорил. Потому как при нынешней производительности МК решение задачи упирается в производительность человека-программиста. Это ведь ему предстоит отлавливать возможные ошибки в написанной программе. Контроллеру действительно всё равно какой код исполнять - правильный или с ошибками :)

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 18:53:22
tonyk
WatchCat писал(а):Непрофессионалу без многолетнего стажа программирования на плюсах - намного сложнее написать на них безошибочный код и/или найти ошибки если они всё-таки случились.
На С++ достаточно написать функцию, использующую многобайтную арифметику, и прилинковать её к основной программе.

И ещё. Попробуй собрать свой проект "плюсОвым" компилятором, а не сишным. "ПлюсЫ" гораздо строже относятся к преобразованиям типов, глубже смотрят и видят больше ошибок и подозрительных мест. Современные сишные компиляторы тоже хорошо проверяют прогу, но всё-таки не так дотошно.

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 21:56:34
Dimon456
ARV писал(а):слухи о тяжеловесности и медлительности float оказались слухами?
Смотря где и как.

Добавлено after 2 hours 39 minutes 40 seconds:
Если не вдаваться в подробности, можно создать свой тип
Спойлер

Код: Выделить всё

union fixed
{
  uint32_t x;
  struct
  {   
    uint16_t fractional: 16;  
	uint16_t integer: 16;  
  };
};

union fixed f_x;
и

Код: Выделить всё

	f_x.fractional = 0;
	f_x.integer = 23906;
	f_x.x /= 4320;

	uint16_t xx = (f_x.fractional * 1000UL) >> 16;
код выполняется за 690us.
В переменной xx будет дробная часть, а в f_x.integer целая часть после деления.
WatchCat писал(а):Кстати,а как/чем удалось померить время в этих тестах?
время, это переменная величина, оно зависит от тактовой частоты МК, в данный тестах тактовая частота МК была 1МГц.

К сожалению я не знаю как здесь измерить количество затраченных тактов.

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 22:37:24
WatchCat
Вариант с созданием собственного типа конечно крут,но я думаю что для моих чисто радиолюбительских целей пока что проще всего будет
применить
a = b * 1000UL / c
А дальнейшей оптимизацией заняться когда и если обнаружится явная необходимость в ней. Может быть еще и не обнаружится.

Re: Правильное выполнение арифметических операций?

Добавлено: Ср мар 23, 2022 22:42:11
Jack_A
fixed:
выполнено за 1,1180ms
6 операций?

float:
выполнено за 0.580us
Меньше чем за один машинный цикл?

Слушайте, как говорили у нас в деревне "Не трэба нас дурыць!" Я даже не говорю: спаять и проверить осциллом на железе. Достаточно штатного АВРовского симулятора, и от этого БСК и пыли не останется.
Сложно посмотреть сгенерированный машинный код? Подозреваю, что что умный компилятор во втором случае, глянув, что результат получается делением двух констант - оптимизнул и запулил в aa ... результат деления, полученный ещё на этапе компиляции.
Вот чем хорош язык высокого уровня, да ещё с классами и объектами: написал пару строчек - а что там получилось в результирующем коде - хрен с ним, не моё это собачье царское дело. Компилятор обязан изваять всё в лучшем виде - не так, как написал, а как задумал.
Sorry если чо не то сказал. Моё мнение - мнение ретрограда, я с ним категорически не согласен, не принимайте во внимание. :shock:

Re: Правильное выполнение арифметических операций?

Добавлено: Чт мар 24, 2022 00:00:50
AlexS4
насчет неверойатного удобства и скорости разработки моргалок светодиодами на gcc я тоже ретроград :)))
и еще больший ретроград когда говорят что пара сотен команд вместо 10 никому же не помешают, это экономия на спичках, подумаешь жрать будет втрое больше, зато ж как здорово все быстро можно разрабатывать, да хоть каждыдень разрабатывай, пока не заработает таки 8)

>>.58e-6*20e6
11.6 тоесть 11.6 тактов @20Mhz
но как поделить ни 32бит ни 40бит) флоты на avr сете 11ю командами я сходу придумать не могу, позже гляну в avr-gcc как определен / для флотов.

с другой стороны у Димона волотайл было написано, посему я не стал сразу выдвигать версию про константирование при компиляции,
но чтото явно нетак, пахнет подлогом :write: , но думаю ненамеренно ;) ... ну или решил Dimon456 подоброму потролить нас, лошков :))
Dimon456 писал(а):AlexS4, сравнительный тест показывает
Спойлер

Код: Выделить всё

p*=1000;
за пределами uint16_t, соответственно p уже будет uint32_t.

Код: Выделить всё

volatile uint16_t z,a,b;
volatile uint32_t  p;

	a= 23906;
	b= 4320;
	z=a/b; 
	p=a%b; 
	p*=1000;	
	p/=b;
выполнено за 1,1180ms

Код: Выделить всё

volatile float aa,bb;

	aa = 23906;
	bb = 4320;
	aa /= bb;
выполнено за 0.580us

то есть float выполняется в 2 раза быстрее.

Re: Правильное выполнение арифметических операций?

Добавлено: Чт мар 24, 2022 07:11:58
tonyk
Dimon456 писал(а):Если не вдаваться в подробности, можно создать свой тип
Это не тип данных, а какая-то отрыжка.
Вот тип данных и набор операций с ним:
Спойлер

Код: Выделить всё

#pragma once

#include "targetver.h"

/**
 * Big integer class, optimized for decimal integers.
 * Stores and manipulates integers represented as byte arrays,
 * where each byte is a decimal digit. If you're looking for
 * robust, bug-free, efficient code, keep looking. This is a quick
 * and dirty hack. Some day I'll write a templatized BigInt, where
 * you will be able to select the base in which to store the 
 * number. When that day comes, most of this code will be thrown
 * away.
 *
 * BUGS:
 *      operator-(int) does not work.
 *
 *      BigInt doesn't play nice with long long. Either use int
 *      or string.
 *
 * INVARIANTS:
 *      - capacity is never smaller than 16
 *      - capacity is not the smallest it can be because every
 *         modifying member function first grows digits as much as
 *         it might ever need and then does its job.
 * FIELD TESTING:
 *      - Passed numerous problems on Valladolid, including
 *          107, 288, 324, 424, 465, 485, 495, 560, 619, 623, etc.
 *
 * COMPATIBILITY:
 *      - This class was written for the g++ compiler and uses some
 *          of the g++ extensions (like "long double" and the ">?="
 *          operator). If you want to compile this under Micro$oft's
 *          "compiler", I don't want to talk to you.
 *
 * LAST MODIFIED: October 5, 2005
 *
 * This file is part of my library of algorithms found here:
 *      http://www.palmcommander.com:8081/tools/
 * LICENSE:
 *      http://www.palmcommander.com:8081/tools/LICENSE.html
 * Copyright (c) 2002-2004
 * Contact author:
 *      igor at cs.ubc.ca
 **/

//using namespace std;

#ifndef __BIGINTEGER_H__
#define __BIGINTEGER_H__

#include <stdio.h>

//#include <string.h>
//#include <string>
#include <iostream>
#include <math.h>

#include "targetver.h"

#ifdef _WIN32_

    #include <sstream>

#endif

#ifdef __MINIOS7__

/*
#ifndef RWSTD_NO_LONG_HEADER_NAME
# include <strstream.h>
#else
# include <strstrea.h>
#endif
*/
#endif

#ifndef min
#define min(x,y) ((x) < (y) ? (x) : (y))
#endif

#ifndef max
#define max(x,y) ((x) > (y) ? (x) : (y))
#endif

class BigInt
{
    private:
        char *digits;
        int size;            // number of used bytes (digits)
        int capacity;        // size of digits
        int sign;            // -1, 0 or +1

    public:
        /** Creates a BigInt with initial value n and initial capacity cap **/
        BigInt( long n, int cap );

        /** Creates a BigInt with initial value n **/
        BigInt( long n );

        /** Creates a BigInt with initial value floor( d ) **/
        //BigInt( long double d );

        /** Creates a BigInt with value 0 **/
        BigInt();

#ifndef __MINIOS7__

        /** Creates a BigInt by reading the value from a string **/
//        BigInt( string s );

        /** Creates a BigInt by reading the value from a C string **/
//        BigInt( const char s[] );

#endif
        /** Copy constructor **/
        BigInt( const BigInt &n );

        /** Assignment operators **/
        const BigInt &operator=( const BigInt &n );
        const BigInt &operator=( long n );

        /** Cleans up **/
        ~BigInt();

        /** Removes any leading zeros and adjusts the sign **/
        void normalize();

        /** Returns the sign of n: -1, 0 or 1 **/
        static int sig( long n );

        /** Returns the sign of n: -1, 0 or 1 **/
        //static int sig( long double n );

        /** Returns the number of decimal digits **/
        inline int length() { return size; }

        /** Arithmetic **/
        BigInt operator++();
        BigInt operator++( int );
        BigInt operator--();
        BigInt operator--( int );
        BigInt operator-();
        BigInt operator+ ( long n    );
        BigInt operator+ ( BigInt n );
        BigInt&operator+=( long n    );
        BigInt&operator+=( BigInt n );
        BigInt operator- ( long n    );
        BigInt operator- ( BigInt n );
        BigInt&operator-=( long n    );
        BigInt&operator-=( BigInt n );
        BigInt operator* ( long n    );
        BigInt operator* ( BigInt n );
        void   operator*=( long n    );
        void   operator*=( BigInt n );
        BigInt operator/ ( long n    );
        BigInt operator/ ( BigInt n );
        void   operator/=( long n    );
        void   operator/=( BigInt n );
        long   operator% ( long n    );
        BigInt operator% ( BigInt n );
        void   operator%=( long n    );
        void   operator%=( BigInt n );
        long divide( long n );              // Divides storing quotient in *this and returning the remainder
        BigInt divide( BigInt n );        // Divides storing quotient in *this and returning the remainder
        //BigInt operator* ( long double n ); // Multiplies by a double and truncates (always under-estimates!)
        //void   operator*=( long double n ); // Multiplies by a double and truncates (always under-estimates!)

        /** Bitwise arithmetic **/
        BigInt operator<< ( int n    );
        void   operator<<=( int n    );
        BigInt operator>> ( int n    );   // Works differently for negative numbers
        void   operator>>=( int n    );   // Works differently for negative numbers
/*
        BigInt operator&  ( int n    );
        BigInt operator&  ( BigInt n );
        void   operator&= ( int n    );
        void   operator&= ( BigInt n );
        BigInt operator|  ( int n    );
        BigInt operator|  ( BigInt n );
        void   operator|= ( int n    );
        void   operator|= ( BigInt n );
        BigInt operator^  ( int n    );
        BigInt operator^  ( BigInt n );
        void   operator^= ( int n    );
        void   operator^= ( BigInt n );
        BigInt operator~();
*/
        /** Concatenation ;-) **/
        BigInt operator,( int n );
        BigInt operator,( BigInt n );

        /** Casting **/
        bool operator!();
        operator bool();
        //operator int();   //XXX: Don't do this!!! It takes precedence over operator+(int,BigInt)
        //operator string();

        /** Comparison **/
        bool operator<( BigInt n );
        bool operator>( BigInt n );
        bool operator==( BigInt n );
        bool operator<=( BigInt n );
        bool operator>=( BigInt n );
        bool operator<( long n );
        bool operator>( long n );
        bool operator==( long n );
        bool operator<=( long n );
        bool operator>=( long n );
        int compare( BigInt n );

        /** Returns the lowest value as an integer (watch out for overflow) **/
        long toLong( void );

        /** Returns the value as a decimal string **/
        //string toString();
        char* strImage( void );

        /** Outputs decimal value to stdout **/
        void print();

        /** Outputs the decimal value, with commas **/
//        void printWithCommas( ostream &out );

    private:
        /** Expansion **/
        void grow();

    /** I/O Friends **/
//    friend istream &operator>>( istream &in, BigInt &n );
//    friend ostream &operator<<( ostream &out, BigInt n );

    /** Logarithms **/
/*
    friend long double log2( BigInt x, long double epsilon );
    inline friend long double log( BigInt x, long double epsilon );
    inline friend long double log10( BigInt x, long double epsilon );
    inline friend long double lg( BigInt x, long double epsilon );
    inline friend long double ln( BigInt x, long double epsilon );
*/
};

#endif  // __BIGINTEGER_H__

Спойлер

Код: Выделить всё

#include "BigInt.h"
                 
BigInt operator+( long m, BigInt &n )
{
    return n + m;
}

BigInt operator-( long m, BigInt &n )
{
    return -n + m;
}

BigInt operator*( long m, BigInt &n )
{
    return n * m;
}

BigInt operator/( long m, BigInt &n )
{
    return BigInt( m ) / n;
}

BigInt operator%( long m, BigInt &n )
{
    return BigInt( m ) % n;
}

/** Misc **/
inline bool isDigit( int c )
{
    return( c >= ( int )'0' && c <= ( int )'9' );
}

/** Input/Output **/
/*
istream &operator>>( istream &in, BigInt &n )           // FIXME: see inside
{
    n.size = 0;
    n.sign = 1;
    int sign = 1;
    int c;
    while( ( c = in.peek() ) >= 0 &&
           ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) )
        in.get();
    if( c < 0 || ( c != ( int )'-' && !isDigit( c ) ) )
    {
        in >> c;                // XXX: force in.fail()
        return in;
    }
    if( c == ( int )'-' ) { sign = -1; in.get(); }

    // FIXME: Extremely inefficient! Use a string.
    while( ( c = in.peek() ) >= 0 && isDigit( c ) )
    {
        in.get();
        n *= 10L;
        n += ( c - ( int )'0' );
    }
    n.sign = sign;      //XXX: assign n.sign directly after fixing the FIXME
    n.normalize();
    return in;
}


ostream &operator<<( ostream &out, BigInt n )       //FIXME: make more efficient
{
    return out << n.toString();
}
*/

BigInt::BigInt( long n, int cap )
{
    cap = max( cap, sizeof( n ) * 8 );
    capacity = cap;
    sign = sig( n );
    n *= sign;
    digits = new char[cap];
    memset( digits, 0, cap );
    for( size = 0; n; size++ )
    {
        digits[size] = (char)(n % 10);
        n /= 10;
    }
}

BigInt::BigInt( long n )
{
    capacity = 64;
    sign = sig( n );
    n *= sign;
    digits = new char[capacity];
    memset( digits, 0, capacity );
    size = 0;
    while( n )
    {
        digits[size++] = (char)(n % 10);
        n /= 10;
    }
}

/*
BigInt::BigInt( long double d )
{
    capacity = 32;
    sign = ( d < 0 ? -1 : d > 0 ? 1 : 0 );
    d *= sign;
    digits = new char[capacity];
    memset( digits, 0, capacity );
    size = 0;
    d = floor( d );
    while( d > 0 )
    {
        int tmp1 = ( int )( ( d - floor( d / 10 ) * 10 ) + 0.5 ),
            tmp2 = ( 0 > tmp1 ? 0 : tmp1 );

        //digits[size++] = 0 >? ( int )( ( d - floor( d / 10 ) * 10 ) + 0.5 ) <? 9;
        digits[size++] = ( tmp2 < 9 ? tmp2 : 9 );
        d = floor( d / 10 );
    }
}
*/

BigInt::BigInt()
{
    capacity = 64;
    sign = 0;
    digits = new char[capacity];
    memset( digits, 0, capacity );
    size = 0;
}

#ifndef __MINIOS7__
/*
BigInt::BigInt( string s )
{
    capacity = max( ( int )s.size(), 16 );
    sign = 0;
    digits = new char[capacity];
    memset( digits, 0, capacity );

    istringstream in( s );
    in >> ( *this );
}

BigInt::BigInt( const char s[] )
{
    capacity = max( ( int )strlen( s ), 16 );
    sign = 0;
    digits = new char[capacity];
    memset( digits, 0, capacity );

    istringstream in( s );
    in >> ( *this );
}
*/
#endif

BigInt::BigInt( const BigInt &n )
{
    capacity = n.capacity;
    sign = n.sign;
    size = n.size;
    digits = new char[capacity];
    memcpy( digits, n.digits, capacity );
}

const BigInt &BigInt::operator=( const BigInt &n )
{
    if( &n != this )
    {
        if( capacity < n.size )
        {
            capacity = n.capacity;
            delete [] digits;
            digits = new char[capacity];
        }
        sign = n.sign;
        size = n.size;
        memcpy( digits, n.digits, size );
        memset( digits + size, 0, capacity - size );
    }
    return *this;
}

const BigInt &BigInt::operator=( long n )
{
    sign = sig( n );
    n *= sign;
    for( size = 0; n; size++ )
    {
        digits[size] = (char)(n % 10);
        n /= 10;
    }
    return *this;
}

BigInt::~BigInt()
{
    delete [] digits;
}

void BigInt::normalize()
{
    while( size && !digits[size-1] ) size--;
    if( !size ) sign = 0;
}

int BigInt::sig( long n )
{
    return( n > 0 ? 1 : ( n == 0 ? 0 : -1 ) );
}

/*
int BigInt::sig( long double n )
{
    return( n > 0 ? 1 : ( n == 0 ? 0 : -1 ) );
}
*/

long BigInt::toLong( void )
{
    int result = 0;
    for( int i = size - 1; i >= 0; i-- )
    {
        result *= 10;
        result += digits[i];
        if( result < 0 ) return sign * 0x7FFFFFFFL;
    }
    return sign * result;
}

/*
string BigInt::toString()
{
    string s = ( sign >= 0 ? "" : "-" );
    for( int i = size - 1; i >= 0; i-- )
        s += ( digits[i] + '0' );
    if( size == 0 ) s += '0';
    return s;
}
*/

char* BigInt::strImage( void )
{
    static char buff[ 512 ];
    int i = 0;

    if( sign < 0 )
        buff[ i++ ] = '-';

    int j;

    for( j = size - 1; j >= 0; j-- )
         buff[ i++ ] = digits[ j ] + '0';

    if( size == 0 )
         buff[ i++ ] = '0';

    buff[ i ] = 0;

    return buff;
}

/*
void BigInt::print()        //FIXME: make more efficient
{
    cout << toString();
}
*/
/*
void BigInt::printWithCommas( ostream &out )
{
    if( sign < 0 ) out.put( '-' );
    for( int i = size - 1; i >= 0; i-- )
    {
        out.put( (unsigned char)(digits[i] + '0') );
        if( !( i % 3 ) && i ) out.put( ',' );
    }
    if( size == 0 ) out.put( '0' );
}
*/
void BigInt::grow()
{
    char *olddigits = digits;
    int oldCap = capacity;
    capacity *= 2;
    digits = new char[capacity];
    memcpy( digits, olddigits, oldCap );
    memset( digits + oldCap, 0, oldCap );
    delete [] olddigits;
}

BigInt BigInt::operator++()
{
    operator+=( 1 );
    return *this;
}

BigInt BigInt::operator++( int )
{
    return operator++();
}

BigInt BigInt::operator--()
{
    operator-=( 1 );
    return *this;
}

BigInt BigInt::operator--( int )
{
    return operator--();
}

BigInt BigInt::operator-()
{
    BigInt result( *this );
    result.sign *= -1;
    return result;
}

BigInt BigInt::operator+( long n )
{
    BigInt result( *this );
    result += n;
    return result;
}

BigInt BigInt::operator+( BigInt n )
{
    BigInt result( *this );
    result += n;
    return result;
}

BigInt &BigInt::operator+=( long n )
{
    if( size == capacity ) grow();

    int nsign = sig( n );
    if( !nsign ) return *this;
    if( !sign ) sign = nsign;
    if( sign == nsign )
    {
        n *= nsign;
        long carry = 0;
        int i;
        for( i = 0; n || carry; i++ )
        {
            long dig = n % 10;
            long newdig = digits[i] + dig + carry;
            digits[i] = (char)(newdig % 10);
            carry = newdig / 10;
            n /= 10;
        }
        size = max( i, size );
    }
    else operator-=( -n );
    return *this;
}

BigInt &BigInt::operator+=( BigInt n )
{
    int maxS = max( size, n.size ) + 1;
    while( maxS >= capacity ) grow();        //FIXME: this is stupid

    if( !n.sign ) return *this;
    if( !sign ) sign = n.sign;
    if( sign == n.sign )
    {
        int carry = 0;
        int i;
        for( i = 0; i < maxS - 1 || carry; i++ )
        {
            int newdig = carry;
            if( i < size ) newdig += digits[i];
            if( i < n.size ) newdig += n.digits[i];
            digits[i] = newdig % 10;
            carry = newdig / 10;
        }
        size = max( i, size );
    }
    else
    {
        n.sign *= -1;
        operator-=( n );
        n.sign *= -1;
    }
    return *this;
}

BigInt BigInt::operator-( long n )
{
    BigInt result( *this );
    result -= n;
    return result;
}

BigInt BigInt::operator-( BigInt n )
{
    BigInt result( *this );
    result -= n;
    return result;
}

BigInt &BigInt::operator-=( long n )
{
    if( size == capacity ) grow();

    int nsign = sig( n );
    if( !nsign ) return *this;
    if( !sign ) sign = 1;
    if( sign == nsign )
    {
        BigInt bin = n;
        if( sign >= 0 && *this < bin || sign < 0 && *this > bin )
        {
            // Subtracting a bigger number
            operator=( toLong() - n );
            return *this;
        }

        n *= nsign;
        int carry = 0;
        int i;
        for( i = 0; n || carry; i++ )
        {
            long dig = n % 10;
            long newdig = digits[i] - dig + carry;
            if( newdig < 0 ) newdig += 10, carry = -1;
            else carry = 0;
            digits[i] = (char)newdig;
            n /= 10;
        }
        normalize();
    }
    else operator+=( -n );
    return *this;
}

BigInt &BigInt::operator-=( BigInt n )
{
    int maxS = max( size, n.size ) + 1;
    while( maxS >= capacity ) grow();        //FIXME: this is stupid

    if( !n.sign ) return *this;
    if( !sign ) sign = 1;
    if( sign == n.sign )
    {
        if( sign >= 0 && *this < n || sign < 0 && *this > n )
        {
            // Subtracting a bigger number
            BigInt tmp = n;
            tmp -= *this;
            *this = tmp;
            sign = -sign;
            return *this;
        }

        int carry = 0;
        int i;
        for( i = 0; i < maxS - 1; i++ )
        {
            int newdig = carry;
            if( i < size ) newdig += digits[i];
            if( i < n.size ) newdig -= n.digits[i];
            if( newdig < 0 ) newdig += 10, carry = -1;
            else carry = 0;
            digits[i] = newdig;
        }
        if( carry )     // Subtracted a bigger number, need to flip sign
        {
            if( i ) digits[0] = 10 - digits[0];
            size = ( i ? 1 : 0 );
            for( int j = 1; j < i; j++ )
            {
                digits[j] = 9 - digits[j];
                if( digits[i] ) size = j + 1;
            }
            sign *= -1;
        }
        normalize();
    }
    else
    {
        n.sign *= -1;
        operator+=( n );
        n.sign *= -1;
    }
    return *this;
}

BigInt BigInt::operator*( long n )
{
    BigInt result( 0, size + sizeof( n ) * 8 );
    int nsign = sig( n );
    n *= nsign;
    result.sign = sign * nsign;
    if( !result.sign ) return result;

    int i, j;
    for( i = 0; n; i++ )
    {
        long dig = n % 10;
        if( dig )
        {
            long carry = 0;
            for( j = 0; j < size || carry; j++ )
            {
                long newDig = result.digits[i + j] + ( j < size ? dig * digits[j] : 0 ) + carry;
                result.digits[i + j] = (char)(newDig % 10);
                carry = newDig / 10;
            }
        }
        n /= 10;
    }
    result.size = i + j - 1;
    return result;
}

BigInt BigInt::operator*( BigInt n )
{
    BigInt result( 0, size + n.size );

    result.sign = sign * n.sign;
    if( !result.sign ) return result;

    int i, j;
    for( i = 0; i < n.size; i++ )
    {
        if( n.digits[i] )
        {
            int carry = 0;
            for( j = 0; j < size || carry; j++ )
            {
                int newDig =
                    result.digits[i + j] +
                    ( j < size ? n.digits[i] * digits[j] : 0 ) +
                    carry;
                result.digits[i + j] = newDig % 10;
                carry = newDig / 10;
            }
        }
    }
    result.size = i + j - 1;

    return result;
}

void BigInt::operator*=( long n )
{
    operator=( operator*( n ) );
}

void BigInt::operator*=( BigInt n )
{
    operator=( operator*( n ) );
}

BigInt BigInt::operator/( long n )
{
    if( !n ) n /= n;        //XXX: force a crash

    BigInt result( *this );
    result /= n;
    return result;
}

BigInt BigInt::operator/( BigInt n )
{
    if( !n ) n.size /= n.size;       //XXX: force a crash

    BigInt result( *this );
    result /= n;
    return result;
}

void BigInt::operator/=( long n )
{
    divide( n );
}

void BigInt::operator/=( BigInt n )
{
    divide( n );
}

long BigInt::operator%( long n )
{
    BigInt tmp( *this );
    return tmp.divide( n );
}

void BigInt::operator%=( long n )
{
    operator=( divide( n ) );
}

BigInt BigInt::operator%( BigInt n )
{
    BigInt tmp( *this );
    return tmp.divide( n );
}

void BigInt::operator%=( BigInt n )
{
    operator=( divide( n ) );
}

long BigInt::divide( long n )
{
    if( !n ) n /= n;        //XXX: force a crash

    int nsign = sig( n );
    n *= nsign;
    if( !sign ) return 0;
    sign *= nsign;

    long tmp = 0;
    for( int i = size - 1; i >= 0; i-- )
    {
        tmp *= 10;
        tmp += digits[i];
        digits[i] = (char)(tmp / n);
        tmp -= digits[i] * n;
    }
    normalize();
    return tmp;
}

BigInt BigInt::divide( BigInt n )
{
    if( !n ) n.size /= n.size;         //XXX: force a crash

    if( !sign ) return 0;
    sign *= n.sign;

    int oldSign = n.sign;
    n.sign = 1;

    BigInt tmp( 0, size );
    for( int i = size - 1; i >= 0; i-- )
    {
        tmp *= 10;
        tmp += digits[i];
        digits[i] = 0;
        while( tmp >= n ) { tmp -= n; digits[i]++; }
    }
    normalize();

    n.sign = oldSign;

    return tmp;
}

// This is only exact to the first 15 or so digits, but it is
// never an over-estimate
/*
BigInt BigInt::operator*( long double n )
{
    // the number of digits after the decimal point to use
    const int DIGS_AFTER_DOT = 15;

    int nsign = sig( n );
    n *= nsign;
    int ndigs = n >= 1 ? ( int )log10( n ) + 1 : 0;
    BigInt result( 0, size + ndigs );
    result.sign = sign * nsign;
    if( !result.sign ) return result;

    if( n >= 1 ) for( int i = 0; i < ndigs; i++ ) n /= 10;
    result.size = 0;

    char afterDot[DIGS_AFTER_DOT + 1];
    memset( afterDot, 0, sizeof( afterDot ) );

    // Keep going until the DIGS_AFTER_DOT'th digit after the decimal point
    for( int i = ndigs - 1; i >= -DIGS_AFTER_DOT; i-- )
    {
        n *= 10;
        int dig = ( int )floor( n );
        n -= dig;
        if( !dig ) continue;

        int carry = 0;
        for( int j = 0; j < size || carry; j++ )
        {
            int newdig =
                ( i + j < 0 ? afterDot[-( i + j )] : result.digits[i + j] )
                + dig * digits[j]
                + carry;
            ( i + j < 0 ? afterDot[-( i + j )] : result.digits[i + j] ) = newdig % 10;
            if( i + j >= 0 && result.digits[i + j] )
            {
                //result.size >?= i + j + 1;
                result.size = max( result.size, i + j + 1 );
            }
            carry = newdig / 10;
        }
    }
    if( !result.size ) result.sign = 0;
    return result;
}

void BigInt::operator*=( long double n )
{
    operator=( operator*( n ) );
}
*/

BigInt BigInt::operator<<( int n )
{
    BigInt result( *this );
    result <<= n;
    return result;
}

void BigInt::operator<<=( int n )
{
    if( n < 0 ) operator>>=( -n );
    else if( n > 0 )
    {
        BigInt mult( 1, 4 * n );
        for( int i = ( 1 << 30 ); i; i >>= 1 )
        {
            mult *= mult;
            if( n & i ) mult *= 2;
        }
        operator*=( mult );
    }
}

BigInt BigInt::operator>>( int n )
{
    BigInt result( *this );
    result >>= n;
    return result;
}

void BigInt::operator>>=( int n )
{
    if( n < 0 ) operator<<=( -n );
    else if( n > 0 )
    {
        BigInt mult( 1, 4 * n );
        for( int i = ( 1 << 30 ); i; i >>= 1 )
        {
            mult *= mult;
            if( n & i ) mult *= 2;
        }
        operator/=( mult );
    }
}
/*
BigInt BigInt::operator&( int n )
{
}

BigInt BigInt::operator&( BigInt n )
{
}

void BigInt::operator&=( int n )
{
}

void BigInt::operator&=( BigInt n )
{
}

BigInt BigInt::operator|( int n )
{
}

BigInt BigInt::operator|( BigInt n )
{
}

void BigInt::operator|=( int n )
{
}

void BigInt::operator|=( BigInt n )
{
}

BigInt BigInt::operator^( int n )
{
}

BigInt BigInt::operator^( BigInt n )
{
}

void BigInt::operator^=( int n )
{
}

void BigInt::operator^=( BigInt n )
{
}

BigInt BigInt::operator~()
{
}
*/
BigInt BigInt::operator,( int n )
{
    BigInt result( 0, size + ( int )sizeof( n ) * 8 );
    for( result.size = 0; n; result.size++ )
    {
        result.digits[result.size] = n % 10;
        n /= 10;
    }
    memcpy( result.digits + result.size, digits, size * sizeof( digits[0] ) );
    result.size += size;
    result.sign = 1;
    result.normalize();
    return result;
}

BigInt BigInt::operator,( BigInt n )
{
    BigInt result( 0, size + n.size );
    memcpy( result.digits, n.digits, n.size * sizeof( n.digits[0] ) );
    memcpy( result.digits + n.size, digits, size * sizeof( digits[0] ) );
    result.size = size + n.size;
    result.sign = 1;
    result.normalize();
    return result;
}

bool BigInt::operator!()
{
    return !size;
}

BigInt::operator bool()
{
    return( size ? true : false );
}

//BigInt::operator int()
//{
//    return toInt();
//}
/*
BigInt::operator string()
{
    return toString();
}
*/
bool BigInt::operator<( BigInt n )
{
    return( compare( n ) < 0 );
}

bool BigInt::operator>( BigInt n )
{
    return( compare( n ) > 0 );
}

bool BigInt::operator==( BigInt n )
{
    return( compare( n ) == 0 );
}

bool BigInt::operator<=( BigInt n )
{
    return( compare( n ) <= 0 );
}

bool BigInt::operator>=( BigInt n )
{
    return( compare( n ) >= 0 );
}

bool BigInt::operator<( long n )
{
    return( compare( BigInt( n ) ) < 0 );
}

bool BigInt::operator>( long n )
{
    return( compare( BigInt( n ) ) > 0 );
}

bool BigInt::operator==( long n )
{
    return( compare( BigInt( n ) ) == 0 );
}

bool BigInt::operator<=( long n )
{
    return( compare( BigInt( n ) ) <= 0 );
}

bool BigInt::operator>=( long n )
{
    return( compare( BigInt( n ) ) >= 0 );
}

int BigInt::compare( BigInt n )
{
    if( sign < n.sign ) return -1;
    if( sign > n.sign ) return 1;
    if( size < n.size ) return -sign;
    if( size > n.size ) return sign;
    for( int i = size - 1; i >= 0; i-- )
    {
        if( digits[i] < n.digits[i] ) return -sign;
        else if( digits[i] > n.digits[i] ) return sign;
    }
    return 0;
}

/*
long double log2( BigInt x, long double epsilon = 0.000000000000001 )
{
    static const  long double O = 0.0;
    if( x.sign <= 0 ) return O / O;     // Return NaN

    long double y = 0.0, z = 1.0, f = 0.0;
    while( x >= 2 )
    {
        if( x.divide( 2 ) ) f += 1.0;
        f /= 2.0;
        y++;
    }
    f += 1.0;
    while( z > epsilon )
    {
        f *= f;
        z /= 2.0;
        if( f >= 2.0 )
        {
            y += z;
            f /= 2.0;
        }
    }
    return y;
}


inline long double log( BigInt x, long double epsilon = 0.000000000000001 )
{
    return log2( x, epsilon ) * 0.6931471805599;
}

inline long double log10( BigInt x, long double epsilon = 0.000000000000001 )
{
    return log2( x, epsilon ) * 0.301029995664;
}

inline long double lg( BigInt x, long double epsilon = 0.000000000000001 )
{
    return log2( x, epsilon );
}

inline long double ln( BigInt x, long double epsilon = 0.000000000000001 )
{
    return log( x, epsilon );
}
*/

Re: Правильное выполнение арифметических операций?

Добавлено: Чт мар 24, 2022 07:15:26
Dimon456
Jack_A писал(а):Меньше чем за один машинный цикл?
В потемках 0 не заметил.
Jack_A писал(а):Сложно посмотреть сгенерированный машинный код?
СпойлерИзображениеИзображение
Атмега8 тактовая 1МГц
tonyk писал(а):Это не тип данных, а какая-то отрыжка.
За то просто и эффективно.