【问题标题】:Trying to understand simple big number calculations试图理解简单的大数计算
【发布时间】:2016-02-24 10:13:22
【问题描述】:

我正在努力更好地了解“大数字”库的工作原理(例如 GMP)。

我想写自己的函数到Add()/Subtract()/Multiply()/Divide()

类是传统定义的......

std::vector<unsigned char> _numbers; // all the numbers
bool _neg; // positive or negative number
long _decimalPos; // where the decimal point is located
                  // so 10.5 would be 1
                  //    10.25 would be 2
                  //    10 would be 0 for example

首先我需要将数字标准化,这样我才能做到

使用 2 个数字 10(x) + 10.25(y) = 20.25

为简单起见,我会让它们的长度相同,

对于 x: _numbers = (1,0,0,0) 十进制 = 2

对于 y: _numbers = (1,0,2,5) 十进制 = 2

然后我可以在循环中反向添加 x 到 y

...
// where x is 10.00 and y is 10.25
...
unsigned char carryOver = 0;
int totalLen = x._numbers.size();
for (size_t i = totalLen; i > 1 ; --i )
{
    unsigned char sum = x._numbers[i-1] + y._numbers[i-1] + carryOver;
    carryOver = 0;
    if (sum > _base)
    {
     sum -= _base;
     carryOver = 1;
    }
    numbers.insert( number.begin(), sum);
}

// any left over?
if (carryOver > 0)
{
  numbers.insert( number.begin(), 1 );
}

// decimal pos is the same for this number as x and y

...

上面的示例适用于将两个正数相加,但一旦我需要将一个负数与一个正数相加,就会很快失败。

当涉及到减法时,这会变得更加复杂,而对于乘法和除法则更糟。

有人可以建议一些简单的函数来加()/减()/乘()/除()

我不是想重写/改进库,我只是想了解它们如何处理数字。

【问题讨论】:

  • 如果你知道如何用铅笔和纸做长算术,你就不需要图书馆。如果您不这样做,图书馆将无法帮助您。
  • 至少你处理的是简单而不是复杂的数字.. :P
  • 您是否尝试过实现这种功能? (在gmplib.org/devel/repo-usage.html 上),您可能会比我们在这里用 2 或 3 行给出的实现细节有更好的了解。尽管如此,我会说人们编写 lib 正是因为他们不想在使用 lib 时为实现的复杂性而烦恼。如果事情是微不足道的而且非常简单,可能你甚至不会考虑将它们变成一个库。
  • 是的,我确实看过它,但出于显而易见的原因,他们优化了很多代码,并且在其中一些优化中丢失了简单计算的完成方式。我只是想尝试了解其中一些库的逻辑。
  • @FFMG 编辑完成

标签: c++ math computation-theory bignum


【解决方案1】:

加法和减法非常简单

您需要检查操作数的符号和大小,并在需要时将操作转换为/从+/-。我的典型 C++ 实现是这样的:

//---------------------------------------------------------------------------
arbnum arbnum::operator + (const arbnum &x)
    {
    arbnum c;
    // you can skip this if you do not have NaN or Inf support
    // this just handles cases like adding inf or NaN or zero
    if (  isnan() ) return *this;
    if (x.isnan() ) { c.nan(); return c; }
    if (  iszero()) { c=x; return c; }
    if (x.iszero()) return *this;
    if (  isinf() ) { if (x.isinf()) { if (sig==x.sig) return *this;
                    c.nan(); return c; } return *this; }
    if (x.isinf()) { c.inf(); return c; }
    // this compares the sign bits if both signs are the same it is addition
    if (sig*x.sig>0) { c.add(x,this[0]); c.sig=sig; }
    // if not
    else{
        // compare absolute values (magnitudes)
        if (c.geq(this[0],x)) // |this| >= |x| ... return (this-x)
                {
                c.sub(this[0],x); 
                c.sig=sig;    // use sign of the abs greater operand
                }
        else    {             // else return (x-this)
                c.sub(x,this[0]);
                c.sig=x.sig;
                } 
        }
    return c;
    }
//---------------------------------------------------------------------------
arbnum arbnum::operator - (const arbnum &x)
    {
    arbnum c;
    if (  isnan() ) return *this;
    if (x.isnan() ) { c.nan(); return c; }
    if (  iszero()) { c=x; c.sig=-x.sig; return c; }
    if (x.iszero()) return *this;
    if (  isinf() ) { if (x.isinf()) { if (sig!=x.sig) return *this;
                    c.nan(); return c; } return *this; }
    if (x.isinf()) { c.inf(); c.sig=-x.sig;  return c; }
    if (x.sig*sig<0) { c.add(x,this[0]); c.sig=sig; }
    else{
        if (c.geq(this[0],x))
                {
                c.sub(this[0],x);
                c.sig=sig;
                }
        else    {
                c.sub(x,this[0]);
                c.sig=-x.sig;
                }
        }
    return c;
    }
//---------------------------------------------------------------------------

地点:

  • geq 是无符号比较大于或等于
  • add 未签名 +
  • sub 未签名 -

除法有点复杂

见:

  • bignum divisions
  • approximational bignum divider

    对于部门,您需要已经实现了诸如 +,-,*,&lt;&lt;,&gt;&gt; 之类的东西,而对于一些更高级的方法,您甚至需要诸如:绝对比较(无论如何+/- 都需要它们),sqr,使用的比特数,通常用于小数部分和整数部分。

    最重要的是乘法,请参阅Fast bignum square computation,因为它是大多数除法算法的核心。

性能

一些提示见BigInteger numbers implementation and performance

文本转换

如果您的号码是 ASCII 或 BASE=10^n 数字,那么这很容易,但如果您出于性能原因使用 BASE=2^n,那么您需要能够在 dechex 字符串之间转换的快速函数,所以您实际上可以在课堂上加载和打印一些数字。见:

【讨论】:

  • 非常感谢,这真的很有帮助!
猜你喜欢
  • 1970-01-01
  • 2014-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-31
  • 2017-08-10
  • 1970-01-01
相关资源
最近更新 更多