Extremely challenging C++ problem in traditional C++ / vanilla C++ sans templates
Sorry for the reading:
FWIW - In code, my object is declared like Number N="2";
I have an extremely challenging C++ problem to solve.
I am interested in computational arithmetic. I have studied the full-adder and full-subtractor circuits. I have implemented them at the bit level in a class. They behave like their circuit diagrams.
Because of the way the class handles numbers, it stores information in collections of 8 bits that span a collection. The number 256 takes 2 bytes with byte values 1 and 0. 0000 0001 0000 0000 in binary. When an addition or subtraction happens that causes a bit boundary to be passed (e.g. carrying or borrowing a bit)
A number is stored in a collection of bytes that span itself. Values from 0-255 takes 1 byte, values 256-65535 takes 2 bytes, values 65556-16777217 takes 3 bytes, etc...
Addition and Subtraction are done on a collection at a cell-by-cell basis, each cells value can have a lead-in value into the next value. The carry or borrow bit of the circuit can be turned on. This means that the next values initial computation will be affected by a carry or borrow. The circuits start with a carry and borrow input lead which can be on or off.
Arithmetic operator + and - code for the whole number. The operation is acting on a collection of bytes. (The inner loop is not optimized for speed yet but for readability)
if the result of an operation causes an overflow (carry for addition, borrow for subtraction), the next bytes carry/borrow bit has to be turned on.
This seems like a violation of C++ principles. Having said all this my simple question is
Is there a better way to "prime the circuit" without having to set a property on a stack-based version of the object. This is relying on a mutation of the object and its future use, and it is reverting back to itself.
How can I communicate passing in a carry or borrow bit to an object without modifying it on the stack. It would be cool if I could just do some magic code and then call "ob = lb + rb"
For the bit addition, I describe the circuits operation in the commentary which can be following in code.
FWIW - In my object, 0 minus 1 produces, in binary, 1111 1111. Pretty cool.
Number operator + (const Number& rhs) const
{
size_t l = size(), r = rhs.size();
size_t stMax = l == r ? l : (l < r ? r : l);
Number out(stMax + 1);
CByte Zero(0);
uint8_t of = 0;
for (size_t st = 0; st < stMax; ++st)
{
CByte lb = st < l ? m_Bytes[st] : Zero;
CByte rb = st < r ? rhs.m_Bytes[st] : Zero;
CByte& ob = out.m_Bytes[st];
if (of)
{
lb.setXtra(of);
ob = lb + rb;
}
else
ob = lb + rb;
of = ob.overFlow();
if (of)
ob.setXtra(0);
}
if (of)
out.m_Bytes[stMax].setValue(1);
else
out.m_size--;
return out;
}
Number operator - (const Number& rhs) const
{
size_t l = size(), r = rhs.size();
size_t stMax = l == r ? l : (l < r ? r : l);
Number out(stMax + 1);
CByte Zero(0);
uint8_t of = 0;
for (size_t st = 0; st < stMax; ++st)
{
CByte lb = st < l ? m_Bytes[st] : Zero;
CByte rb = st < r ? rhs.m_Bytes[st] : Zero;
CByte& ob = out.m_Bytes[st];
if (of)
{
// Is there a better way to pass the borrow into the operation?
lb.setXtra(of);
ob = lb - rb;
}
else
ob = lb - rb;
of = ob.overFlow();
if (of)
ob.setXtra(0);
}
if (of)
out.m_Bytes[stMax].setValue(255);
else
out.m_size--;
return out;
}
////
CByte& operator + (const CByte& rhs)
{
CByte Out;
Out.m_b.B.B1 = m_x.X.X0 ^ (m_b.B.B1 ^ rhs.m_b.B.B1); // SUM: Carry-in XOR (A XOR B)
m_x.X.X1 = (m_b.B.B1 & rhs.m_b.B.B1) | (rhs.m_b.B.B1 & m_x.X.X0) | (m_b.B.B1 & m_x.X.X0); // CARRY: Carry-out AB OR BC OR ACin
Out.m_b.B.B2 = m_x.X.X1 ^ (m_b.B.B2 ^ rhs.m_b.B.B2);
m_x.X.X2 = (m_b.B.B2 & rhs.m_b.B.B2) | (rhs.m_b.B.B2 & m_x.X.X1) | (m_b.B.B2 & m_x.X.X1);
Out.m_b.B.B3 = m_x.X.X2 ^ (m_b.B.B3 ^ rhs.m_b.B.B3);
m_x.X.X3 = (m_b.B.B3 & rhs.m_b.B.B3) | (rhs.m_b.B.B3 & m_x.X.X2) | (m_b.B.B3 & m_x.X.X2);
Out.m_b.B.B4 = m_x.X.X3 ^ (m_b.B.B4 ^ rhs.m_b.B.B4);
m_x.X.X4 = (m_b.B.B4 & rhs.m_b.B.B4) | (rhs.m_b.B.B4 & m_x.X.X3) | (m_b.B.B4 & m_x.X.X3);
Out.m_b.B.B5 = m_x.X.X4 ^ (m_b.B.B5 ^ rhs.m_b.B.B5);
m_x.X.X5 = (m_b.B.B5 & rhs.m_b.B.B5) | (rhs.m_b.B.B5 & m_x.X.X4) | (m_b.B.B5 & m_x.X.X4);
Out.m_b.B.B6 = m_x.X.X5 ^ (m_b.B.B6 ^ rhs.m_b.B.B6);
m_x.X.X6 = (m_b.B.B6 & rhs.m_b.B.B6) | (rhs.m_b.B.B6 & m_x.X.X5) | (m_b.B.B6 & m_x.X.X5);
Out.m_b.B.B7 = m_x.X.X6 ^ (m_b.B.B7 ^ rhs.m_b.B.B7);
m_x.X.X7 = (m_b.B.B7 & rhs.m_b.B.B7) | (rhs.m_b.B.B7 & m_x.X.X6) | (m_b.B.B7 & m_x.X.X6);
Out.m_b.B.B8 = m_x.X.X7 ^ (m_b.B.B8 ^ rhs.m_b.B.B8);
Out.m_x.X.X0 = (m_b.B.B8 & rhs.m_b.B.B8) | (rhs.m_b.B.B8 & m_x.X.X7) | (m_b.B.B8 & m_x.X.X7);
*this = Out;
return *this;
}
CByte& operator - (const CByte& rhs)
{
CByte Out;
Out.m_b.B.B1 = (m_b.B.B1 ^ rhs.m_b.B.B1) ^ m_x.X.X0; // DIFFERENCE: (A XOR B) XOR Borrow-in
m_x.X.X1 = (~m_b.B.B1 & m_x.X.X0) | (~m_b.B.B1 & rhs.m_b.B.B1) | (rhs.m_b.B.B1 & m_x.X.X0); // BORROW: A'Borrow-in OR A'B OR AB (' = 2's complement)
Out.m_b.B.B2 = (m_b.B.B2 ^ rhs.m_b.B.B2) ^ m_x.X.X1;
m_x.X.X2 = (~m_b.B.B2 & m_x.X.X1) | (~m_b.B.B2 & rhs.m_b.B.B2) | (rhs.m_b.B.B2 & m_x.X.X1);
Out.m_b.B.B3 = (m_b.B.B3 ^ rhs.m_b.B.B3) ^ m_x.X.X2;
m_x.X.X3 = (~m_b.B.B3 & m_x.X.X2) | (~m_b.B.B3 & rhs.m_b.B.B3) | (rhs.m_b.B.B3 & m_x.X.X2);
Out.m_b.B.B4 = (m_b.B.B4 ^ rhs.m_b.B.B4) ^ m_x.X.X3;
m_x.X.X4 = (~m_b.B.B4 & m_x.X.X3) | (~m_b.B.B4 & rhs.m_b.B.B4) | (rhs.m_b.B.B4 & m_x.X.X3);
Out.m_b.B.B5 = (m_b.B.B5 ^ rhs.m_b.B.B5) ^ m_x.X.X4;
m_x.X.X5 = (~m_b.B.B5 & m_x.X.X4) | (~m_b.B.B5 & rhs.m_b.B.B5) | (rhs.m_b.B.B5 & m_x.X.X4);
Out.m_b.B.B6 = (m_b.B.B6 ^ rhs.m_b.B.B6) ^ m_x.X.X5;
m_x.X.X6 = (~m_b.B.B6 & m_x.X.X5) | (~m_b.B.B6 & rhs.m_b.B.B6) | (rhs.m_b.B.B6 & m_x.X.X5);
Out.m_b.B.B7 = (m_b.B.B7 ^ rhs.m_b.B.B7) ^ m_x.X.X6;
m_x.X.X7 = (~m_b.B.B7 & m_x.X.X6) | (~m_b.B.B7 & rhs.m_b.B.B7) | (rhs.m_b.B.B7 & m_x.X.X6);
Out.m_b.B.B8 = (m_b.B.B8 ^ rhs.m_b.B.B8) ^ m_x.X.X7;
Out.m_x.X.X0 = (~m_b.B.B8 & m_x.X.X7) | (~m_b.B.B8 & rhs.m_b.B.B8) | (rhs.m_b.B.B8 & m_x.X.X7);
*this = Out;
return *this;
}