以浮点/双精度表示非常大的数字

时间:2020-08-18 14:43:40

标签: c#

我想在一个应用程序(增量游戏)中显示非常大的数字。指数可以大于100,000(十万),因此doublefloat将满足其限制。我知道BigInteger类型,但我读到它可能很慢,并且我不需要在有效位数上超过5 digts的精度,因此它可能不是一个好选择

数字的示例可以是1.2345 * 10 ^ 100000。在这种情况下,我不在乎第5位之后的任何小数(精度在5-15 digt之间的任何地方都可以)

我需要在30ms循环中对该数字执行多项操作(乘法,加法,乘方,对数),因此它应该很快。我怎样才能做到这一点呢?有图书馆吗?还是需要自己做?

如果我能用科学,工程,字母,标准(百万,十亿,...,十亿,八十万,非百万)等不同的符号表示数字,那也很好。

2 个答案:

答案 0 :(得分:1)

我不知道是否已经有一个用于此的库,但是您可以尝试为LargeNumbers编写自己的类:

class LargeNumber
{
    double Value;
    int Mantissa;
}

然后,您可以覆盖加法,乘法运算符等。
确定您必须在那里做一些数学运算

编辑: 是的,您必须了解指数是如何工作的,这并不琐碎,但也不太困难。

加法运算的一些数学示例,例如:
firstNumber.Mantissa> secondNumber.Mantissa> 0:

...
var expDifference = firstNumber.Mantissa - secondNumber.Mantissa;
if(expDifference > 5)
{
   return firstNumber;
}
if(expDifference < 5)
{
   return new LargeNumber
   {
       Value = firstNumber.Value*(10^expDifference) + secondNumber.Value,
       Mantissa= firstNumber.Mantissa
   }
}
...

答案 1 :(得分:0)

一种近似表示巨大数字的想法是仅使用数字的对数。 通常的算术运算非常简单地转换为对数。我们利用以下公式:

  • log( a * b )= log( a )+ log( b
  • log( a / b )= log( a )-log( b
  • log( a ^ b )= b * log( a

最复杂的是加法/减法(从数字上讲,选择较大的数字为 a 是有利的)

  • log( a + b )= log( a *(1 + b / a ))= log( a )+ log(1 + b / a ))

由于对数仅定义为严格的正数,因此为了表示负数和零,我们必须将符号添加为额外的簿记变量。

总共,我提出了以下类,实现加,减,一元加减,乘法,除法,对数,求幂和一整套比较运算符:

struct HugeNumber : IEquatable<HugeNumber>, IComparable<HugeNumber>
{
    private int _sign; // -1, 0 or +1
    private double _log;
    
    private HugeNumber(int sign, double log)
    {
        _sign = sign;
        _log = log;
    }
    
    public static implicit operator HugeNumber(double number)
    {
        if (number > 0.0)
            return new HugeNumber(1, Math.Log10(number));
        else if (number < 0.0)
            return new HugeNumber(-1, Math.Log10(-number));
        else
            return new HugeNumber(0, 0.0);
    }
    
    public static HugeNumber operator*(HugeNumber n1, HugeNumber n2)
    {
        return new HugeNumber(n1._sign * n2._sign, n1._log + n2._log);
    }
    
    public static HugeNumber operator/(HugeNumber n1, HugeNumber n2)
    {
        return new HugeNumber(n1._sign * n2._sign, n1._log - n2._log);
    }
    
    public static HugeNumber operator+(HugeNumber n)
    {
        return n;
    }
    
    public static HugeNumber operator-(HugeNumber n)
    {
        return new HugeNumber(-n._sign, n._log);
    }
    
    public static HugeNumber operator+(HugeNumber n1, HugeNumber n2)
    {
        if (n1._sign == 0)
        {
            return n2;
        }
        else if (n2._sign == 0)
        {
            return n1;
        }
        else 
        {
            if (n1._log > n2._log)
            {
                return new HugeNumber(n1._sign, n1._log + Math.Log10(1.0 + n1._sign * n2._sign * Math.Pow(10, n2._log - n1._log)));
            }
            else
            {
                return new HugeNumber(n2._sign, n2._log + Math.Log10(1.0 + n1._sign * n2._sign * Math.Pow(10, n1._log - n2._log)));
            }
        }
    }
    
    public static HugeNumber operator-(HugeNumber n1, HugeNumber n2)
    {
        return n1 + -n2;
    }
    
    public double Log10()
    {
        if (_sign <= 0)
            throw new ArgumentOutOfRangeException();
        return _log;
    }
    
    public HugeNumber Pow(double exponent)
    {
        if (_sign < 0)
            throw new ArgumentOutOfRangeException();
        return new HugeNumber(_sign, exponent * _log);
    }
    
    public HugeNumber Abs()
    {
        if (_sign >= 0)
            return this;
        return new HugeNumber(1, _log);
    }

    public override string ToString()
    {
        if (_sign == 0)
            return "0";
        else
            return string.Format("{0}{1}E{2:+0;-#}", _sign < 0 ? "-" : "", Math.Pow(10, _log - Math.Floor(_log)), Math.Floor(_log));
    }
    
    public bool Equals(HugeNumber n)
    {
        if (_sign == 0)
            return n._sign == 0;
        return _sign == n._sign && _log == n._log;
    }
    
    public override bool Equals(object obj)
    {
        if (obj is HugeNumber n)
            return Equals(n);
        return false;
    }
    
    public override int GetHashCode()
    {
        if (_sign == 0)
            return 0;
        return _log.GetHashCode();
    }
    
    public int CompareTo(HugeNumber n)
    {
        if (_sign > n._sign)
            return 1;
        else if (_sign < n._sign)
            return -1;
        else if (_sign == 0)
            return 0;
        else
            return _sign * _log.CompareTo(n._log);
    }
    
    public static bool operator<(HugeNumber n1, HugeNumber n2) => n1.CompareTo(n2) < 0;
    public static bool operator<=(HugeNumber n1, HugeNumber n2) => n1.CompareTo(n2) <= 0;
    public static bool operator>(HugeNumber n1, HugeNumber n2) => n1.CompareTo(n2) > 0;
    public static bool operator>=(HugeNumber n1, HugeNumber n2) => n1.CompareTo(n2) >= 0;
    public static bool operator==(HugeNumber n1, HugeNumber n2) => n1.Equals(n2);
    public static bool operator!=(HugeNumber n1, HugeNumber n2) => !n1.Equals(n2);
}

用法:

HugeNumber n = 10;
Console.WriteLine(n.Pow(10000)); // prints 1E+10000
相关问题