我正在用C ++构建一个小的BigInt库,用于我的编程语言。
结构如下:
short digits[ 1000 ];
int len;
我有一个函数,通过将字符串拆分为单个字符并将它们放入digits
,将字符串转换为bigint。
数字中的数字都是相反的,因此数字123将如下所示:
digits[0]=3 digits[1]=3 digits[2]=1
我已经设法对添加功能进行编码,该功能完美无缺。
它有点像这样:
overflow = 0
for i ++ until length of both numbers exceeded:
add numberA[ i ] to numberB[ i ]
add overflow to the result
set overflow to 0
if the result is bigger than 10:
substract 10 from the result
overflow = 1
put the result into numberReturn[ i ]
(在这种情况下溢出就是当我添加1到9时会发生什么:从10中减去10,向溢出添加1,溢出被添加到下一个数字)
所以想想如何存储两个数字,如:
0 | 1 | 2
---------
A 2 - -
B 0 0 1
以上代表bigints 2(A)和100(B)的digits
。
-
表示未初始化的数字,不会访问它们。
所以添加上面的数字工作正常:从0开始,加2 + 0,转1,加0,转2,加1
可是:
当我想与上述结构进行乘法运算时,我的程序最终会执行以下操作:
从0开始,将2与0(eek)相乘,转到1,...
所以很明显,对于乘法,我必须得到这样的订单:
0 | 1 | 2
---------
A - - 2
B 0 0 1
然后,一切都会清楚:从0开始,将0乘以0,转到1,将0乘以0,转到2,乘以2
digits
转换为正确的乘法形式?答案 0 :(得分:4)
short
来存储[0..9]
char
中的数字就足够了B
与A
中的每个数字相乘,并将它们相加,并以10的正确幂进行移位。编辑:由于一些匿名者在没有评论的情况下对此进行了下调,这基本上就是乘法算法:
bigint prod = 0
for i in A
prod += B * A[i] * (10 ^ i)
B
与A[i]
的乘法是通过额外的for循环完成的,您还可以跟踪进位。 (10 ^ i)
是通过offseting目标索引实现的,因为bigint位于基数10。
答案 1 :(得分:4)
在我看来,问题中的例子是过度工程。由于所涉及的乘法和加法的剪切次数,您的方法最终会比正常的长乘法更慢。当你每次可以增加大约9个时,不要限制自己只能在一个基数位上工作!将base10字符串转换为hugeval,然后然后对其执行操作。 不要直接对字符串执行操作。你会发疯的。这是一些演示加法和乘法的代码。更改M
以使用更大的类型。你也可以使用std :: vector,但是你会错过一些优化。
#include <iostream>
#include <string>
#include <algorithm>
#include <sstream>
#include <cstdlib>
#include <cstdio>
#include <iomanip>
#ifdef _DEBUG
#include <assert.h>
#define ASSERT(x) assert(x)
#else
#define ASSERT(x)
#endif
namespace Arithmetic
{
const int M = 64;
const int B = (M-1)*32;
struct Flags
{
Flags() : C(false),Z(false),V(false),N(false){}
void Clear()
{
C = false;
Z = false;
V = false;
N = false;
}
bool C,Z,V,N;
};
static unsigned int hvAdd(unsigned int a, unsigned int b, Flags& f)
{
unsigned int c;
f.Clear();
//b = -(signed)b;
c = a + b;
f.N = (c >> 31UL) & 0x1;
f.C = (c < a) && (c < b);
f.Z = !c;
f.V = (((signed)a < (signed)b) != f.N);
return c;
}
static unsigned int hvSub(unsigned int a, unsigned int b, Flags& f)
{
unsigned int c;
f.Clear();
c = a - b;
//f.N = ((signed)c < 0);
f.N = (c >> 31UL) & 0x1;
f.C = (c < a) && (c < b);
f.Z = !c;
f.V = (((signed)a < (signed)b) != f.N);
return c;
}
struct HugeVal
{
HugeVal()
{
std::fill(part, part + M, 0);
}
HugeVal(const HugeVal& h)
{
std::copy(h.part, h.part + M, part);
}
HugeVal(const std::string& str)
{
Flags f;
unsigned int tmp = 0;
std::fill(part, part + M, 0);
for(unsigned int i=0; i < str.length(); ++i){
unsigned int digit = (unsigned int)str[i] - 48UL;
unsigned int carry_last = 0;
unsigned int carry_next = 0;
for(int i=0; i<M; ++i){
tmp = part[i]; //the value *before* the carry add
part[i] = hvAdd(part[i], carry_last, f);
carry_last = 0;
if(f.C)
++carry_last;
for(int j=1; j<10; ++j){
part[i] = hvAdd(part[i], tmp, f);
if(f.C)
++carry_last;
}
}
part[0] = hvAdd(part[0], digit, f);
int index = 1;
while(f.C && index < M){
part[index] = hvAdd(part[index], 1, f);
++index;
}
}
}
/*
HugeVal operator= (const HugeVal& h)
{
*this = HugeVal(h);
}
*/
HugeVal operator+ (const HugeVal& h) const
{
HugeVal tmp;
Flags f;
int index = 0;
unsigned int carry_last = 0;
for(int j=0; j<M; ++j){
if(carry_last){
tmp.part[j] = hvAdd(tmp.part[j], carry_last, f);
carry_last = 0;
}
tmp.part[j] = hvAdd(tmp.part[j], part[j], f);
if(f.C)
++carry_last;
tmp.part[j] = hvAdd(tmp.part[j], h.part[j], f);
if(f.C)
++carry_last;
}
return tmp;
}
HugeVal operator* (const HugeVal& h) const
{
HugeVal tmp;
for(int j=0; j<M; ++j){
unsigned int carry_next = 0;
for(int i=0;i<M; ++i){
Flags f;
unsigned int accum1 = 0;
unsigned int accum2 = 0;
unsigned int accum3 = 0;
unsigned int accum4 = 0;
/* Split into 16-bit values */
unsigned int j_LO = part[j]&0xFFFF;
unsigned int j_HI = part[j]>>16;
unsigned int i_LO = h.part[i]&0xFFFF;
unsigned int i_HI = h.part[i]>>16;
size_t index = i+j;
size_t index2 = index+1;
/* These multiplications are safe now. Can't overflow */
accum1 = j_LO * i_LO;
accum2 = j_LO * i_HI;
accum3 = j_HI * i_LO;
accum4 = j_HI * i_HI;
if(carry_next){ //carry from last iteration
accum1 = hvAdd(accum1, carry_next, f); //add to LSB
carry_next = 0;
if(f.C) //LSB produced carry
++carry_next;
}
/* Add the lower 16-bit parts of accum2 and accum3 to accum1 */
accum1 = hvAdd(accum1, (accum2 << 16), f);
if(f.C)
++carry_next;
accum1 = hvAdd(accum1, (accum3 << 16), f);
if(f.C)
++carry_next;
if(carry_next){ //carry from LSB
accum4 = hvAdd(accum4, carry_next, f); //add to MSB
carry_next = 0;
ASSERT(f.C == false);
}
/* Add the higher 16-bit parts of accum2 and accum3 to accum4 */
/* Can't overflow */
accum4 = hvAdd(accum4, (accum2 >> 16), f);
ASSERT(f.C == false);
accum4 = hvAdd(accum4, (accum3 >> 16), f);
ASSERT(f.C == false);
if(index < M){
tmp.part[index] = hvAdd(tmp.part[index], accum1, f);
if(f.C)
++carry_next;
}
carry_next += accum4;
}
}
return tmp;
}
void Print() const
{
for(int i=(M-1); i>=0; --i){
printf("%.8X", part[i]);
}
printf("\n");
}
unsigned int part[M];
};
}
int main(int argc, char* argv[])
{
std::string a1("273847238974823947823941");
std::string a2("324230432432895745949");
Arithmetic::HugeVal a = a1;
Arithmetic::HugeVal b = a2;
Arithmetic::HugeVal d = a + b;
Arithmetic::HugeVal e = a * b;
a.Print();
b.Print();
d.Print();
e.Print();
system("pause");
}
答案 2 :(得分:1)
Andreas是对的,你必须将一个数字乘以另一个数字,并相应地对结果求和。最好将较长的数字乘以我认为较短的数字。如果你在你的数组中存储十进制数字char就足够了,但如果你想要性能,也许你应该考虑更大的类型。我不知道你的平台是什么,但是以x86为例你可以使用32位整数和硬件支持来提供32位乘法的64位结果。
答案 3 :(得分:0)