#c#
#c#
Вопрос:
Я хотел бы показывать очень большие числа в приложении (инкрементная игра). Показатель степени может быть больше 100 000 (ста тысяч), поэтому double
или float
будет соответствовать их ограничениям. Я знаю о BigInteger
типе, но я читал, что он может быть медленным, и мне не нужно более 5 значений точности для значения, так что это может быть не очень хорошим вариантом
Примером числа может быть 1.2345 * 10 ^ 100000. В этом случае меня не волнует любая десятичная дробь после 5-го (где-то между 5-15 цифрами точности было бы нормально)
Мне нужно выполнить несколько операций (умножение, сложение, мощность, журнал) над числом в цикле 30 мс, поэтому это должно быть быстро. Как я могу сделать это хорошим способом? Существуют ли какие-либо библиотеки или мне нужно создать свою собственную?
Было бы также здорово, если бы я мог представить число с различными обозначениями, такими как научные, инженерные, алфавитные, стандартные (миллион, миллиард, …, септиллион, октиллион, нониллион) и т.д.
Комментарии:
1. «показатель степени может быть больше 100 000 (ста тысяч), поэтому double или float будут соответствовать их ограничениям». — потому что в вашей вселенной 100.000 больше, чем 10 exp 308? Похоже, вы никогда не читали об ограничениях типа данных double, ИЛИ вы действительно НЕ высказываете здесь свою точку зрения. Из того, что вы говорите, вы уже закончили с double, и в чем-либо еще нет необходимости.
2. Из любопытства: где встречаются такие большие числа? Я вроде как помню, что количество элементарных частиц во Вселенной равно 10 ^ 80 или около того.
3. @TomTom Показатель больше 100 000. Да, в этой вселенной 100 000 намного больше, чем 308 🙂
4. @TomTom 10 ^ 100000 намного больше, чем 10 ^ 308, и явно выходит за пределы диапазона double.
5. Взгляните сюда: gamedev.stackexchange.com/questions/114911 /…
Ответ №1:
Я не знаю, есть ли уже библиотека для этого, но вы можете попробовать написать свой собственный класс для LargeNumbers:
class LargeNumber
{
double Value;
int Mantissa;
}
Затем вы можете переопределить операторы сложения, умножения и т.д.
Конечно, вам нужно немного посчитать там
Редактировать: Да, вы должны знать о том, как работают экспоненты, и это не тривиально, но и не слишком сложно.
Пример некоторой математики для операции сложения, в случае, когда:
firstNumber.Мантисса> второе число.Мантисса> 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. Хм, да. Это было бы хорошим ответом, если бы вы немного углубились в «некоторую математику».
2. Это тоже была моя мысль. Я просто надеялся, что эта библиотека уже существует 🙂
3. То, что вы вызываете,
Mantissa
обычно должно быть названоExponent
.
Ответ №2:
Одна из идей приблизительного представления огромных чисел — работать только с логарифмом числа. Обычные арифметические операции довольно просто переносятся в логарифм. Мы используем следующие формулы:
- 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 amp;amp; _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
Комментарии:
1.
CompareTo(HugeNumber n) ... return _sign * _log.CompareTo(n._log);
?2. @JeremyLakeman вы правы — спасибо, что заметили эту ошибку!