Содержимое файла LongCalculation.cpp




//---------------------------------------------------------------------------

 

#include <vcl.h>

#pragma hdrstop

 

#include "LongCalculation.h"

//---------------------------------------------------------------------------

//--- CLongUnsignedInteger --------------------------------------------------

CLongUnsignedInteger::CLongUnsignedInteger(): FValue("0") {

};

CLongUnsignedInteger::CLongUnsignedInteger(CLongUnsignedInteger& oValue) {

SetValue(oValue.Value);

};

CLongUnsignedInteger& CLongUnsignedInteger::operator =(CLongUnsignedInteger oValue){

SetValue(oValue.Value);

Normalize();

return *this;

};

 

/**

* Операция сложения двух целых чисел.

*

* Теория:

* Для сложения необходимо выравнять числа по правому краю, а затем поразрядно сложить их.

* Может получиться и такая ситуация, когда при сложении чисел одного разряда получится число больше 9

* В этом случае в сам разряд необходимо записать остаток от деления числа на 10, а само число

* разделить на 10 и сложить с суммоу чисел следующего разряда.

*

* Примечания:

* 1- в символьной таблице символы цифр хранятся последовательно, от символа 0 к символу 9.

* В виду этого будет очень удобно получать числовые эквиваленты символов через их разницу

* с символом нуля. (пример: '9' - '0' == 9; т.к. '9' == 0x39, а '0' == 0x30)

*

*/

CLongUnsignedInteger CLongUnsignedInteger::operator +(CLongUnsignedInteger oValue){

// результат операции.

CLongUnsignedInteger oResult;

// позиция первого разряда левого операнда

int iLeftPos = FValue.Length();

// позиция первого разряда правого операнда

int iRightPos = oValue.Value.Length();

// буфер операции и буфер переполнения в одном лице

int iOFlag = 0;

oResult.Value = "";

// в пределах определения обоих операндов производим поразрядное сложение

while(iLeftPos && iRightPos){

int iLeft = FValue[iLeftPos--] - '0';

int iRight = oValue.Value[iRightPos--] - '0';

// Вычисляем значение текущего разряда и записываем его в результат

iOFlag += iLeft + iRight;

oResult.PushDigit('0' + (char)(iOFlag % 10));

// Корректируем значение с сохранением переполненниых разрядов

iOFlag /= 10;

};

// остается только переписать неучтенные старшие разряды, если один из операндов длиннее второго.

// разряды должны быть так же переписаны через буфер переполнения.

if(iLeftPos){

while(iLeftPos){

iOFlag += (FValue[iLeftPos--] - '0');

oResult.PushDigit('0' + (char)(iOFlag % 10));

iOFlag /= 10;

};

}else if(iRightPos){

while(iRightPos){

iOFlag += (oValue.Value[iRightPos--] - '0');

oResult.PushDigit('0' + (char)(iOFlag % 10));

iOFlag /= 10;

};

};

// записываем остаток в буфере, если он есть

if(iOFlag) oResult.PushDigit('0' + (char)iOFlag);

// выполняем нормализацию

oResult.Normalize();

return oResult;

};

 

/**

* Операция вычитания одного целого из другого.

*

* Теория:

* Операция аналогична сложению, за исключением одного ограничения.

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

*

* Примечания:

* 1- Если правый операнд больше лоевого, генерируется исключение

* 2- Для изучения деталей механики следует смотреть описания вышеизложенных методов

*

*/

CLongUnsignedInteger CLongUnsignedInteger::operator -(CLongUnsignedInteger oValue){

// результат операции

CLongUnsignedInteger oResult;

// если правый операнд больше левого, генерируем исключение [catch(...)/catch(bool)]

if(*this < oValue) throw false;

// позиция младшего разряда левого операнда

int iLeftPos = FValue.Length();

// подзиция младшего разряда правого операнда

int iRightPos = oValue.Value.Length();

// буфер операции и буфер переполнения в одном лице

int iOFlag;

// флаг переполнения операции, нужен для операции заимствования из старшего разряда

bool bOverflow = false;

oResult.Value = "";

// в пределах определения обоих операндов производим поразрядное вычитание

while(iLeftPos && iRightPos){

// достаем значение текущего разряда левого операнда,

// корректируем его значением флага переполнения

int iLeft = (FValue[iLeftPos--] - '0') - (int)bOverflow;

// достаем значение текущего разряда правого операнда

int iRight = oValue.Value[iRightPos--] - '0';

// сбрасываем флаг переполнения

bOverflow = false;

// вычисляем значение в текущем разряде

iOFlag = iLeft - iRight;

// если результат меньше нуля, необходимо заимствование из старшего разряда

if(0 > iOFlag){

// имеется переполнение в старшем разряде

bOverflow = true;

// корректируем результат текущей операции

iOFlag += 10;

};

// записываем значение буфера в разряд резултата операции

oResult.PushDigit('0' + (char)(iOFlag % 10));

};

// правый операнд заведомо меньше левого

// Остается только переписать старшие, еще неучтенные, разряды из левого операнда

if(iLeftPos){

while(0 < iLeftPos){

iOFlag = (FValue[iLeftPos--] - '0') - (int)bOverflow;

bOverflow = false;

if(0 > iOFlag){

bOverflow = true;

iOFlag += 10;

};

oResult.PushDigit('0' + (char)(iOFlag % 10));

};

};

// небольшая предосторожность, если все-таки было вывлено переполнение,

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

if(bOverflow) throw false;

// выполняем нормализацию

oResult.Normalize();

return oResult;

};

 

/**

* Операция перемножения двух целых чисел.

*

* Теория:

* Для перемножения двух чисел необходимо каждый разряд правого операнда умножить на левый операнд,

* получить набор чисел и через их сложение получить результат умножения.

* Благо операция сложения длинных чисел уже есть, а поразрядное умножение механически аналогично сложению.

*

* На деле же, каждый результат поразрядного умножения складывается с результатом операции.

* Сам результат операции изначально равен нулю. Никакой проблемы хранения динамических данных.

*

* Примечания:

* 1- Для изучения деталей механики следует смотреть описания вышеизложенных методов.

* 2- Операция имеет M*N трудоемкость, чем больше разрядов, тем дольше перемножать.

*

*/

CLongUnsignedInteger CLongUnsignedInteger::operator *(CLongUnsignedInteger oValue){

// результат вычислений

CLongUnsignedInteger oResult;

// буфер поразрядного умножения

CLongUnsignedInteger oMulBuffer;

// позиция младшего разряда левого операнда

int iLeftPos = FValue.Length();

// позиция младшего разряда правого операнда

int iRightPos = oValue.Value.Length();

// буфер поразрядной операции и буфер переполнения в одном лице

int iOFlag;

oResult.Value = "0";

// каждый разряд правого операнда надо умножить на левый операнд

while(iRightPos){

// Достаем значение в текущем разряде правого операнда

int iRight = oValue.Value[iRightPos--] - '0';

// Сбрасываем значение буфера операции

iOFlag = 0;

// если разряд нулевой, можно не делать рутину за зря и просто перейти к следующему разряду

if(!iRight) continue;

oMulBuffer.Value = "";

// дополняем буфер умножения необходимым числом нулей, согласно позиции разряда правого операнда

for(int iOffset = (oValue.Value.Length() - iRightPos - 1); 0 < iOffset; iOffset--) oMulBuffer.AddDigit('0');

// производим поразрядное умножение

for(int iLeftTrack = iLeftPos; 0 < iLeftTrack; iLeftTrack--){

int iLeft = FValue[iLeftTrack] - '0';

// производим умножение для текущих разрядов и записываем результат в буфер

iOFlag += iLeft * iRight;

oMulBuffer.PushDigit('0' + (char)(iOFlag % 10));

// Корректируем значение с сохранением переполненниых разрядов

iOFlag /= 10;

};

// если есть переполнение разрядов, записываем его в буфер

if(iOFlag) oMulBuffer.PushDigit('0' + (char)(iOFlag % 10));

// производим сложение буфера с результатом операции

oResult = oResult + oMulBuffer;

};

// выполняем нормализацию

oResult.Normalize();

return oResult;

};

 

/**

* Операция деления двух целых чисел.

*

* Теория:

* Удобнее всего целочисленное деление реализовать через поэтапный сдвиг и вычитание.

* К томму же машинный механизм деления базируется на том же, но в побитовом выражении.

*

* Способ (изначально результат операции деления равен нулю):

* - Если левый операнд меньше правого, останавливаем деление и возвращаем результат операции.

* - Увеличиваем число разрядов правого операнда, пока тот не станет больше левого.

* - Уменьшаем число разрядов правого операнда на 1. Запоминаем разряды в счетчике разрядов.

* - Вычитаем правый операнд из левого, результат кладем в левый операнд.

* - Прибавляем к результату операции значение счетчика разрядов.

* - Восстанавливаем изначальное значение правого операнда.

* - Возвращаемся к первому пункту способа.

*

* Примечания:

* 1- X / 0 -- генерируется исключение.

* 2- Если левый операнд изначально меньше правого, операция делния не производится.

* Производится немедленный возврат заранее определнного результата ("0").

*

*/

CLongUnsignedInteger CLongUnsignedInteger::operator /(CLongUnsignedInteger oValue){

// Результат операции

CLongUnsignedInteger oResult;

// Счетчик разрядов

CLongUnsignedInteger oMulBuffer;

// Буфер правого операнда для оперций с поразрядным сдвигом

CLongUnsignedInteger oDivBuffer;

// Левый операнд для изменений в течении вычислений

CLongUnsignedInteger oLeft((CLongUnsignedInteger&)*this);

oResult.Value = "0";

// Если производится деление на ноль, то генерируется исключение

if(oResult == oValue) throw false;

// Проверяем, больше ли левый операнд правого

while(oLeft > oValue){

// буфер для иъятия одного младшего разряда.

// Одновременно - флаг наличия сдвига в правом операнде

char cDigit;

// Восстанавливаем изначальное значение правого операнда, счетчик разрядов - в 1, флаг сдвига сброшен

oDivBuffer = (CLongUnsignedInteger&)oValue;

oMulBuffer.Value = "1";

cDigit = 0;

// производим сдвиг разрядов, запоминаем число сдвигов, пока правый операнд меньше левого

while(oDivBuffer < oLeft){

oDivBuffer.AddDigit('0');

oMulBuffer.AddDigit('0');

cDigit = 1;

};

// [для безопасности] проверяем, были ли сдвиги в правом операнде

// Если сдвигов небыло и правый операнд больше левого, останавливаем деление

if(!cDigit && (oDivBuffer > oLeft)) break;

// уменьшаем число разрядов правого операнда и счетчика разрядов на 1

oDivBuffer.RemoveDigit(cDigit);

oMulBuffer.RemoveDigit(cDigit);

// производим вычитание операндов и суммирование результатов деления

while(oLeft >= oDivBuffer){

oLeft = oLeft - oDivBuffer;

oResult = oResult + oMulBuffer;

};

};

// [для безопасности] если правый операнд все еще больше левого, уменьшаем результат операции на 1

//if(oDivBuffer > *this) oResult--;

// Выполняем нормализацию

oResult.Normalize();

return oResult;

};

 

/**

* Постфиксный инкремент. [ a++ ]

*

* Теория:

* Необходимо запомнить текущее значение числа, произвести инкремент

* и результатом вернуть запмненное значение.

*

*/

CLongUnsignedInteger CLongUnsignedInteger::operator ++(int iType){

// правый операнд, для инкремента

CLongUnsignedInteger oIncr;

// результат операции

CLongUnsignedInteger oResult;

 

oIncr.Value = "1";

// если параметр эквивалентен нулю, значит вызвана постфиксная форма оператора.

if(!iType){

// запоминаем текущее значение

oResult = *this;

// увеличиваем собственное значение на 1

*this = *this + oIncr;

}else{

oResult = *this = *this + oIncr;

};

 

// выполняем нормализацию

Normalize();

oResult.Normalize();

return oResult;

};

 

/**

* Постфиксный декремент. [ a-- ]

*

* Теория:

* Необходимо запомнить текущее значение числа, произвести декремент

* и результатом вернуть запмненное значение.

*

* Примечания:

* 1- "0"-- вызовет исключение. Никаких проверок не производится.

*

*/

CLongUnsignedInteger CLongUnsignedInteger::operator --(int iType){

// правый операнд, для декремента

CLongUnsignedInteger oIncr;

// результат операции

CLongUnsignedInteger oResult;

 

oIncr.Value = "1";

// если параметр эквивалентен нулю, значит вызвана постфиксная форма оператора.

if(!iType){

// запоминаем текущее значение

oResult = *this;

// уменьшаем собственное значение на 1

*this = *this - oIncr;

}else{

oResult = *this = *this - oIncr;

};

 

// выполняем нормализацию

Normalize();

oResult.Normalize();

return oResult;

};

 

/**

* Префиксный инкремент. [ ++a ]

*

* Теория:

* Значение сперва изменяется и возвращается в уже измененном виде.

*

*/

CLongUnsignedInteger CLongUnsignedInteger::operator ++(){

// правый операнд, для инкремента

CLongUnsignedInteger oIncr;

// результат операции

CLongUnsignedInteger oResult;

oIncr.Value = "1";

// инкремент и рекурсивное присваивание

oResult = *this = *this + oIncr;

// выполнение нормализации

Normalize();

oResult.Normalize();

return oResult;

};

 

/**

* Префиксный декремент. [ --a ]

*

* Теория:

* Значение сперва изменяется и возвращается в уже измененном виде.

*

* Примечания:

* 1- --"0" вызовет исключение. Никаких проверок не производится.

*

*/

CLongUnsignedInteger CLongUnsignedInteger::operator --(){

// правый операнд, для декремента

CLongUnsignedInteger oIncr;

// результат операции

CLongUnsignedInteger oResult;

oIncr.Value = "1";

// декремент и рекурсивное присваивание

oResult = *this = *this - oIncr;

// выполнение нормализации

Normalize();

oResult.Normalize();

return oResult;

};

 

/**

* Логическое сравнение эквивалентности.

*

* Теория:

* Достаточно просто сравнить два числа как две строки, в этом случае вскроется даже поразрядное неравенство.

*

*/

bool CLongUnsignedInteger::operator ==(CLongUnsignedInteger& oValue){

return 0 == strcmp(FValue.c_str(), oValue.Value.c_str());

};

 

/**

* Логическое сравнение превосходства.

*

* Теория:

* Для решения задач логики превосходства и недостатка используется алгоритм разности операндов.

* При этом результат операции определяется сперва исходя из длин операндов,

* а потом - согласно значению флага переполнения.

*

* Примечания:

* 1- Для изучения деталей механики следует смотреть описания вышеизложенного оператора разности.

*

*/

bool CLongUnsignedInteger::operator >(CLongUnsignedInteger& oValue){

// позиция младшего разряда левого операнда

int iLeftPos = FValue.Length();

// позиция младшего разряда правого операнда

int iRightPos = oValue.Value.Length();

// флаг переполнения, true - означает наличие переполнения

bool bOverflow = false;

// сравнивать можно только равные по длинам числа.

// другие варианты приводят к заведомо известным значениям

if(iLeftPos > iRightPos) return true;

if((iLeftPos < iRightPos)||(*this == oValue)) return false;

// производим поразрядное вычитание и определние состояния переполнения разрядов.

while(iLeftPos && iRightPos){

int iResult = (int)(FValue[iLeftPos--] - '0') - (int)(oValue.Value[iRightPos--] - '0') - (int)bOverflow;

bOverflow = 0 > iResult;

};

 

// Если переполнение есть, значит правый операнд меньше левого, операция должна вернуть true.

// Иначе необходимо вернуть false.

return!bOverflow;

};

 

/**

* Логическое сравнение недостатка.

*

* Теория:

* Для решения задач логики превосходства и недостатка используется алгоритм разности операндов.

* При этом результат операции определяется сперва исходя из длин операндов,

* а потом - согласно значению флага переполнения.

*

* Примечания:

* 1- Для изучения деталей механики следует смотреть описания вышеизложенного оператора разности.

*

*/

bool CLongUnsignedInteger::operator <(CLongUnsignedInteger& oValue){

// позиция младшего разряда левого операнда

int iLeftPos = FValue.Length();

// позиция младшего разряда правого операнда

int iRightPos = oValue.Value.Length();

// флаг переполнения, true - означает наличие переполнения

bool bOverflow = false;

 

// сравнивать можно только равные по длинам числа.

// другие варианты приводят к заведомо известным значениям

if(iLeftPos < iRightPos) return true;

if((iLeftPos > iRightPos)||(*this == oValue)) return false;

// производим поразрядное вычитание и определние состояния переполнения разрядов.

while(iLeftPos && iRightPos){

int iResult = (int)(FValue[iLeftPos--] - '0') - (int)(oValue.Value[iRightPos--] - '0') - (int)bOverflow;

bOverflow = 0 > iResult;

};

// Если переполнение есть, значит левый операнд меньше правого, операция должна вернуть true.

// Иначе необходимо вернуть false.

return bOverflow;

};

 

/**

* Нормализация числа.

*

* Теория:

* Из числа просто надо удалить все нули слева. Не важно, каким образом они там могли оказаться.

*

*/

void CLongUnsignedInteger::Normalize(){

while((1 < FValue.Length())&&('0' == FValue[1])) FValue.Delete(1,1);

};

 

/**

* Выдача служебного значения числа.

*

*/

AnsiString CLongUnsignedInteger::GetValue(){

return FValue;

};

 

/**

* Установка нового значения числа.

*

*/

AnsiString CLongUnsignedInteger::SetValue(const AnsiString sValue){

FValue = sValue;

Normalize();

return FValue;

};

 

/**

* Добавление одного разряда слева

*

* Теория:

* Первый символ строки означает позицию самого старшего разряда числа.

* Необходимо просто сместить строку вправо и вставить на новый разряд в первый символ.

* Это делается стандартными средствами С++

*

*/

void CLongUnsignedInteger::PushDigit(char cValue){

AnsiString sOpValue(cValue);

FValue.Insert(sOpValue,1);

};

 

/**

* Изъятие одного разряда слева

*

* Теория:

* Из строки изымается первый символ и производится сдвиг строки в влево.

* Это делается стандартными средствами С++

*

*/

void CLongUnsignedInteger::PopDigit(char& cValue){

if(FValue == "0"){

cValue = 0;

return;

};

 

cValue = FValue[1];

FValue.Delete(1,1);

if(!FValue.Length()) FValue = "0";

};

 

/**

* Добавление одного разряда справа

*

* Теория:

* Последний символ строки - самый младший разряд, добавляется к хвосту строки.

* Это делается стандартными средствами С++

*

*/

void CLongUnsignedInteger::AddDigit(char cValue){

AnsiString sOpValue(cValue);

if((FValue == "0") && (cValue!= '0')) FValue = "";

FValue.Insert(sOpValue,FValue.Length()+1);

};

 

/**

* Изъятие одного разряда справа

*

* Теория:

* Из строки изымается самый последний символ, строка обрезается на 1 символ.

* Это делается стандартными средствами С++

*

*/

void CLongUnsignedInteger::RemoveDigit(char& cValue){

if(FValue == "0"){

cValue = 0;

return;

};

 

cValue = FValue[FValue.Length()];

FValue.Delete(FValue.Length(),1);

if(!FValue.Length()) FValue = "0";

};

 

/**

* Пользовательский вывод значения числа.

*

* Теория:

* Целое бесзнаковое число не обладает дополнительными свойствами,

* пользовательское и служебыное значения ни чем не отличаются.

*

*/

AnsiString CLongUnsignedInteger::PrintText(){

Normalize();

return FValue;

};

 

 

//---------------------------------------------------------------------------

//---- CLongSignedInteger ---------------------------------------------------

CLongSignedInteger::CLongSignedInteger(): FSignFlag(false) {

 

};

 

/**

* Конструктор копирования.

*

* Теория:

* Конструктор копирования производного класса должен выполнять те же действия,

* что и конструктор копирования базового класса, для этого необходимо создать

* обратную наследственную связь. Нобходмио увязать конструкторы копирования между собой.

*

*...er& oValue): CLongUnsignedInteger(oValue),...

*

* Данная увязка позволяет однозначно задать конструктор для инициализации облсти базового

* класса внутри области производного класса.

*

*/

CLongSignedInteger::CLongSignedInteger(CLongSignedInteger& oValue): CLongUnsignedInteger(oValue), FSignFlag(oValue.Sign) {

 

};

 

/**

* Обновленный метод пользовательского представления числа.

*

* Теория:

* Данный класс хранит число со знаком, положительное число отображается без знака

* Отрицательное число отображается со знаком "минус".

* Принято, что если FSignFlag установлено в значение true, значит число отрицательно.

* Следовательно знак минуса необходимо подставлять только когда FSignFlag == true.

*

* Примечания:

* 1- Выполняется стандартными средствами С++

*/

AnsiString CLongSignedInteger::PrintText(){

AnsiString sResult;

sResult.printf("%s%s",((FSignFlag)? "-":""),GetValue());

return sResult;

};

 

/**

* Оператор присваивания.

*

* Теория:

* Параметр - это правый операнд, this - это левый операнд.

* Для обеспечения рекурсивного присваивания, оператор должен

* возвращать ссылку на значение, в частности - возвращать *this.

*

* CLongUnsignedInteger::operator =((CLongUnsignedInteger&)oValue);

*

* Для обеспечения совместимости с родительским классом производится вызов

* соответствующего оператора из пространства имен родительского класса.

*

*/

CLongSignedInteger& CLongSignedInteger::operator =(CLongSignedInteger oValue){

CLongUnsignedInteger::operator =((CLongUnsignedInteger&)oValue);

FSignFlag = oValue.Sign;

Normalize();

return *this;

};

 

/**

* Сложение двух знаковых чисел.

*

* Теория:

* Теперь числа обладают знаком и операция сложения усложнилось. Может возникнуть ситуация,

* когда на деле прийдется вовсе не складывать, а вычитать.

*

* Алгоритм (сравнения по модулю, знак числа учитывается отдельно):

* - если оба положительны, выполняется сложение двух простых бесзнаковых, знак результата положителен

* - если оба отрицательны, выполняется сложение двух простых бесзнаковых, знак результата отрицателен

* - если отрицательно только одно из чисел, вычитание простых большего числа из меньшего,

* знак результата как у большего.

*

* Примечания:

* 1- Вычисления оператора основываются на уже реализованных вычислениях бесзнаковых чисел

* 2- Для изучения деталей механики следует смотреть описания вышеизложенного оператора разности.

*/

CLongSignedInteger CLongSignedInteger::operator +(CLongSignedInteger oValue){

// результат операции

CLongSignedInteger oResult;

// левый и правый операнды, представление простого целого

CLongUnsignedInteger &oLeft = (CLongUnsignedInteger&)*this;

CLongUnsignedInteger &oRight = (CLongUnsignedInteger&)oValue;

// если только один из операндов отрицателен, применяем сложную логику

 

if((!FSignFlag && oValue.Sign)||(FSignFlag &&!oValue.Sign)){

 

// проверка терминальная, далее одно из чисел будет обязательно отрицательным

// если левый операнд отрицателен

 

if(FSignFlag){

 

// если слева еще и большее число, знак результата будет отрицательным

oResult.Sign = oLeft > oRight;

// вычитается только меньшее из большего, которое число больше,

// определяется по знаку результата

if(!oResult.Sign) (CLongUnsignedInteger&)oResult = oRight - oLeft;

else (CLongUnsignedInteger&)oResult = oLeft - oRight;

}else{

// левое положительно, значит правое точно отрицательно

// и если правое число будет больше, результат будет отрицательным.

oResult.Sign = oLeft < oRight;

// вычитается только меньшее из большего, которое число больше,

// определяется по знаку результата

if(!oResult.Sign) (CLongUnsignedInteger&)oResult = oLeft - oRight;

else (CLongUnsignedInteger&)oResult = oRight - oLeft;

};

}else{

// делаем простое сложение и устанавливаем знак обоих операндов

(CLongUnsignedInteger&)oResult = oLeft + oRight;

oResult.Sign = FSignFlag || oValue.Sign;

};

// непременное выполнение нормализации

oResult.Normalize();

return oResult;

};

 

/**

* Вычитание двух знаковых чисел.

*

* Теория:

* В случае со знаковыми числами, операция вычитания так же становится нелинейной. Появляется

* случай, когда вычитание становится сложением.

*

* Примечания:

* 1- Вычисления оператора основываются на уже реализованных вычислениях бесзнаковых чисел.

* 2- Для изучения деталей механики следует смотреть описания вышеизложенного оператора разности.

* 3- Логически ликвидирована генерация исключений переполнения.

*

*/

CLongSignedInteger CLongSignedInteger::operator -(CLongSignedInteger oValue){

// результат операции

CLongSignedInteger oResult;

// левый и правый операнды, представление простого целого

CLongUnsignedInteger &oLeft = (CLongUnsignedInteger&)*this;

CLongUnsignedInteger &oRight = (CLongUnsignedInteger&)oValue;

if(oValue.Sign){

// если правый операнд отрицателен,

// получается ситуация А - -Б, которая преобразуется в А + Б

CLongSignedInteger oModule(oValue);

oModule.Sign = false;

oResult = *this + oModule;

}else{

// справа положительное, надо проверить знак левого

if(FSignFlag){

// если слева отрицательное, значит производится сложение в отрицательной плоскости

(CLongUnsignedInteger&)oResult = oLeft + oRight;

oResult.Sign = true;

}else{

// если правое число будет больше, результат операции будет отрицательным.

oResult.Sign = oLeft < oRight;

// вычитается только меньшее из большего, которое число больше,

// определяется по знаку результата

if(!oResult.Sign) (CLongUnsignedInteger&)oResult = oLeft - oRight;

else (CLongUnsignedInteger&)oResult = oRight - oLeft;

};

};

 

// выполнение нормализации

oResult.Normalize();

return oResult;

};

 

/**

* Операция умножения знаковых чисел.

*

* Теория:

* Механически операция ни чем не отличается от перемножения бесзнаковых чисел.

* Основное отличие перемножения чисел со знаком состоит в том, что знак результата

* становится отрицательным только тогда, когда отрицателен лиш один операнд.

*

* Примечания:

* 1- Вычисления оператора основываются на уже реализованных вычислениях бесзнаковых чисел.

* 2- Знак результата определяется по логике XOR, сами числа перемножаются как бесзнаковые.

*

* Терминальная логика операции XOR: XOR | 0 | 1 |

*

*

*/

CLongSignedInteger CLongSignedInteger::operator *(CLongSignedInteger oValue){

// результат операции

CLongSignedInteger oResult;

// левый и правый операнды, представление простого целого

CLongUnsignedInteger &oLeft = (CLongUnsignedInteger&)*this;

CLongUnsignedInteger &oRight = (CLongUnsignedInteger&)oValue;

// выполняется перемножение простых

(CLongUnsignedInteger&)oResult = oLeft * oRight;

// определяется знак результата операции

oResult.Sign = (!FSignFlag && oValue.Sign)||(FSignFlag &&!oValue.Sign);

// непременное выполнение нормализации, избыточное быть может

oResult.Normalize();

return oResult;

};

 

/**

* Операция деления знаковых чисел.

*

* Теория:

* Аналогично механизму перемножения знаковых, деление знаковых так же ни чем

* не отличается от деления бесзнаковых.

*

* Примечания:

* 1- Вычисления оператора основываются на уже реализованных вычислениях бесзнаковых чисел.

* 2- Знак результата определяется по логике XOR, сами числа перемножаются как бесзнаковые.

* 3- Для изучения деталей механики следует смотреть описания вышеизложенных операторов.

*

*/

CLongSignedInteger CLongSignedInteger::operator /(CLongSignedInteger oValue){

// результат оперции

CLongSignedInteger oResult;

// левый и правый операнды, представление простого целого

CLongUnsignedInteger &oLeft = (CLongUnsignedInteger&)*this;

CLongUnsignedInteger &oRight = (CLongUnsignedInteger&)oValue;

// выполняется деление простых

(CLongUnsignedInteger&)oResult = oLeft / oRight;

// определяется знак результата операции

oResult.Sign = (!FSignFlag && oValue.Sign)||(FSignFlag &&!oValue.Sign);

// непременное выполнение нормализации, избыточное быть может

oResult.Normalize();

return oResult;

};

 

/**

* Постфиксный инкремент.

*

* Примечания:

* 1- Для изучения деталей механики следует смотреть описания вышеизложенных операторов.

*

*/

CLongSignedInteger CLongSignedInteger::operator ++(int iType){

// результат оперции

CLongSignedInteger oResult;

// буфер инкремента, единичка для сложения

CLongSignedInteger oIncr;

oIncr.Value = "1";

// постфиксный оператор вызывается с параметром, равным нулю

switch(iType){

case 0:

// запоминаем текущее значение

oResult = *this;

// производим инкремент

*this = *this + oIncr;

break;

};

// выполнение нормализации обоих чисел, избыточное быть может

Normalize();

oResult.Normalize();

return oResult;

};

 

/**

* Постфиксный декремент.

*

* Примечания:

* 1- Для изучения деталей механики следует смотреть описания вышеизложенных операторов.

*

*/

CLongSignedInteger CLongSignedInteger::operator --(int iType){

// результат оперции

CLongSignedInteger oResult;

// буфер декремента, единичка для вычитания

CLongSignedInteger oIncr;

oIncr.Value = "1";

// постфиксный оператор вызывается с параметром, равным нулю

switch(iType){

case 0:

// запоминаем текущее значение

oResult = *this;

// производим декремент

*this = *this - oIncr;

break;

};

 

// эту строчку еще стоит описывать? - нормализацияяя...:)

Normalize();

oResult.Normalize();

return oResult;

};

 

/**

* Префиксный инкремент.

*

* Примечания:

* 1- Для изучения деталей механики следует смотреть описания вышеизложенных операторов.

*

*/

CLongSignedInteger CLongSignedInteger::operator ++(){

// результат оперции

CLongSignedInteger oResult;

// буфер инкремента, единичка для сложения

CLongSignedInteger oIncr;

oIncr.Value = "1";

// рекурсивное присваивание, выполнение инкремента

oResult = *this = *this + oIncr;

// нормализация обоих чисел

Normalize();

oResult.Normalize();

return oResult;

};

 

/**

* Префиксный декремент.

*

* Примечания:

* 1- Для изучения деталей механики следует смотреть описания вышеизложенных операторов.

*

*/

CLongSignedInteger CLongSignedInteger::operator --(){

// результат оперции

CLongSignedInteger oResult;

// буфер декремента, единичка для вычитания

CLongSignedInteger oIncr;

oIncr.Value = "1";

// рекурсивное присваивание, выполнение декремента

oResult = *this = *this - oIncr;

// нормализация обоих чисел

Normalize();

oResult.Normalize();

return oResult;

};

 

/**

* Оператор логической эквивалентности.

*

* Примечания:

* 1- Для изучения деталей механики следует смотреть описания оператора родитльского класса.

*

*/

bool CLongSignedInteger::operator ==(CLongSignedInteger& oValue){

return ((CLongUnsignedInteger&)*this == (CLongUnsignedInteger&)oValue)&&(FSignFlag == oValue.Sign);

};

 

/**

* Оператор сравнения превосходства левого.

*

* Теория:

* Необходимо просто учесть знаки чисел.

* Учтя знаки чисел, сравнение можно реализовать через уже определенные операторы

* сравнения базового класса. Просто надо учесть специфику знака чисел.

*

* 7 > 4, но -7 < -4.

*

* Просто снимаем оба минуса с чисел и меняем знак.

*/

bool CLongSignedInteger::operator >(CLongSignedInteger& oValue){

 

// слева отрицательное, справа положительное, результат однозначно определен

if(FSignFlag &&!oValue.Sign) return false;

// слева положительное, справа отрицательное, результат однозначен

if(!FSignFlag && oValue.Sign) return true;

// далее уже точно известно, что оба числа или положительны, или отрицательны

// опеределяем левый и правый операнды для сравнения по модулю

CLongUnsignedInteger &oLeft = (CLongUnsignedInteger&)*this;

CLongUnsignedInteger &oRight = (CLongUnsignedInteger&)oValue;

// если числа отрицательны, большим числом будет меньшее по модулю

// если числа положительны, то сравнение производится как и обычно

return (FSignFlag)? (oRight > oLeft):(oLeft > oRight);

};

 

/**

* Оператор сравнения превосходства правого.

*

* Примечания:

* 1- Операторы превосходства реализуются абсолютно идентично.

* 2- Для изучения деталей механики следует смотреть описания оператора родитльского класса.

*

*/

bool CLongSignedInteger::operator <(CLongSignedInteger& oValue){

 

// слева положительно, справа отрицательное, результат однозначно определн

if(!FSignFlag && oValue.Sign) return false;

// слева отрицательное, справа положительное, результат так же однозначен

if(FSignFlag &&!oValue.Sign) return true;

 

// опеределяем левый и правый операнды для сравнения по модулю

CLongUnsignedInteger &oLeft = (CLongUnsignedInteger&)*this;

CLongUnsignedInteger &oRight = (CLongUnsignedInteger&)oValue;

// если числа отрицательны, меньшим числом будет большее по модулю

// если числа положительны, то сравнение производится как и обычно

return (FSignFlag)? (oRight < oLeft):(oLeft < oRight);

};

 

//--- CLongFloat ------------------------------------------------------------

CLongFloat::CLongFloat(): FFrac("") {

 

};

 

/**

* Конструктор копирования.

*

* Теория:

* Конструктор наследует логику конструктора копирования знаковых чисел.

*

*/

CLongFloat::CLongFloat(CLongFloat& oValue): CLongSignedInteger((CLongSignedInteger&)oValue) {

SetFrac(oValue.FFrac);

};

 

/**

* Оператор присваивания для вещественных чисел.

*

* Теория:

* Область родительского класса (целая часть и знак) определяется через вызов оператора этого класса.

* Определяется вещественная часть числа.

* В любом случае необходимо сделать нормализацию.

*

*/

CLongFloat& CLongFloat::operator =(CLongFloat oValue){

// вызываем оператор родительского класса

CLongSignedInteger::operator =((CLongSignedInteger)oValue);

// устанавливаем значение дробной части

SetFrac(oValue.Fraction);

 

// выполняем нормализацию

Normalize();

return *this;

};

 

/**

* Оператор сложения двух вещественных.

*

* Теория:

* Для сложения чисел используется предварительное выравнивание дробных частей.

* На бумаге это выглядело бы так: (складываем 36.005 и 3.06)

* Чтоб выравнять дробные части, домножаем левую и правую части на 1000,

* получаем 36005 + 3060.

* Вычисляем 36005 + 3060 == 39065; делим 39065 на 1000 и получаем

* окончательный результат -- 39.065

*

* Примечания:

* 1- Операция полностью базируется на CLognSignedInteger::operator +()

*

*/

CLongFloat CLongFloat::operator +(CLongFloat oValue){

// результат операции

CLongFloat oResult;

// Дубликаты дробных частей операндов, нужны только для выравнивания

CLongFloat oLeftFrac;

CLongFloat oRightFrac;

// левый и правый операнды

CLongSignedInteger oLeft = (CLongSignedInteger&)*this;

CLongSignedInteger oRight = (CLongSignedInteger&)oValue;

// порядок выравнивания дробных разрядов

int iMaxFracLength = (FFrac.Length() > oValue.Fraction.Length())? FFrac.Length():oValue.Fraction.Length();

 

// Переписываем дроби в целые части чисел

oLeftFrac.Fraction = FFrac;

oRightFrac.Fraction = oValue.FFrac;

 

// выравниваем числа по порядку дробных частей.

for(int iTrack = 0; iTrack < iMaxFracLength; iTrack++){

char cDigit;

// вынимаем старший разряд из дроби

oLeftFrac.PopFrac(cDigit);

// если разряд вынулся, кладем его в младший разряд целого

// если не, кладем туда ноль

oLeft.AddDigit((cDigit)? cDigit:'0');

// синхронно и аналогично поступаем и с правым операндом

oRightFrac.PopFrac(cDigit);

oRight.AddDigit((cDigit)? cDigit:'0');

};

 

// операнды выравнены, теперь их достаточно только сложить

// операнды складываются как простые знаковые числа

(CLongSignedInteger&)oResult = oLeft + oRight;

// а после сложения достаточно только вернуть дробные разряды на место

while(iMaxFracLength--){

char cDigit;

// достаем младший разряд из целой

oResult.RemoveDigit(cDigit);

// и кладем его в старший разряд дробной части

// если разряд не достался, кладем в дробь нолик

oResult.PushFrac((cDigit)? cDigit:'0');

};

 

// выполнение нормализации

oResult.Normalize();

return oResult;

};

 

/**

* Оператор разницы двух вещественных.

*

* Теория:

* Сложение и вычитание делаются полностью одинаково.

* Примечания:

* 1- Операция полностью базируется на CLognSignedInteger::operator -()

*

*/

CLongFloat CLongFloat::operator -(CLongFloat oValue){

// результат операции

CLongFloat oResult;

// Дубликаты дробных частей операндов, нужны только для выравнивания

CLongFloat oLeftFrac;

CLongFloat oRightFrac;

// левый и правый операнды

CLongSignedInteger oLeft = (CLongSignedInteger&)*this;

CLongSignedInteger oRight = (CLongSignedInteger&)oValue;

// порядок выравнивания дробных разрядов

int iMaxFracLength = (FFrac.Length() > oValue.Fraction.Length())? FFrac.Length():oValue.Fraction.Length();

 

// Переписываем дроби в целые части чисел

oLeftFrac.Fraction = FFrac;

oRightFrac.Fraction = oValue.FFrac;

 

// выравниваем числа по порядку дробных частей.

for(int iTrack = 0; iTrack < iMaxFracLength; iTrack++){

char cDigit;

// вынимаем старший разряд из дроби

oLeftFrac.PopFrac(cDigit);

// если разряд вынулся, кладем его в младший разряд целого

// если не, кладем туда ноль

oLeft.AddDigit((cDigit)? cDigit:'0');

// синхронно и аналогично поступаем и с правым операндом

oRightFrac.PopFrac(cDigit);

oRight.AddDigit((cDigit)? cDigit:'0');

};

 

// операнды выравнены, теперь их достаточно только сложить

// операнды складываются как простые знаковые числа

(CLongSignedInteger&)oResult = oLeft - oRight;

// а после сложения достаточно только вернуть дробные разряды на место

while(iMaxFracLength--){

char cDigit;

// достаем младший разряд из целой

oResult.RemoveDigit(cDigit);

// и кладем его в старший разряд дробной части

// если разряд не достался, кладем в дробь нолик

oResult.PushFrac((cDigit)? cDigit:'0');

};

 

// выполнение нормализации

oResult.Normalize();

return oResult;

};

 

 

/**

* Оператор умножения дробных

*

* Теория:

* Умножение двух дробных чисел можно сделать через умножение целых,

* выравнив добные части по разрядам. Но это решение будет требовать предела точночти.

* Еще умножение можно представить в виде операции сложения:

*

* aa.bb * xx.yy == (aa*xx) + (bb*yy) + (aa*yy) + (xx*bb)

*

* Таким образом, можно выделить 3 подоперации умножения:

* - умножение двух целых (уже реализовано в родительском классе)

* - умножение двух дробных (MulFra



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-04-11 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: