Last active
August 27, 2021 19:10
-
-
Save mjs3339/6f2706fe712178cfb92725a5b14583f2 to your computer and use it in GitHub Desktop.
Arbitrary Precision Signed BigDecimal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Numerics; | |
using System.Runtime.InteropServices; | |
using System.Text; | |
[Serializable] | |
[DebuggerDisplay("{DDisplay}")] | |
public struct BigDecimal : IComparable, IComparable<BigDecimal>, IEquatable<BigDecimal> | |
{ | |
private const int MaxFactorials = 200; | |
private static int _bitWidth = 64; | |
public static float MaxPrecision = _bitWidth / 64f * 20f; | |
private static readonly BigInteger DoublePrecision = BigInteger.Pow(10, 308); | |
private static readonly BigInteger DoubleMaxValue = (BigInteger)double.MaxValue; | |
private static readonly BigInteger DoubleMinValue = (BigInteger)double.MinValue; | |
private static readonly BigInteger DecimalPrecision = BigInteger.Pow(10, 28); | |
private static readonly BigInteger DecimalMaxValue = (BigInteger)decimal.MaxValue; | |
private static readonly BigInteger DecimalMinValue = (BigInteger)decimal.MinValue; | |
private static BigDecimal[] Factorials; | |
private int _scale; | |
private BigInteger _unscaledValue; | |
public BigDecimal(BigInteger value) : this(value, 0) | |
{ | |
} | |
public BigDecimal(xIntX value) : this((BigInteger)value, 0) | |
{ | |
} | |
public BigDecimal(BigIntX value) : this((BigInteger)value, 0) | |
{ | |
} | |
public BigDecimal(BigInteger value, int scale) | |
{ | |
_unscaledValue = value; | |
_scale = scale; | |
} | |
public BigDecimal(long value) : this(value, 0) | |
{ | |
} | |
public BigDecimal(double value) : this((decimal)value) | |
{ | |
} | |
public BigDecimal(float value) : this((decimal)value) | |
{ | |
} | |
public BigDecimal(byte[] value) | |
{ | |
var number = new byte[value.Length - 4]; | |
var flags = new byte[4]; | |
Array.Copy(value, 0, number, 0, number.Length); | |
Array.Copy(value, value.Length - 4, flags, 0, 4); | |
_unscaledValue = new BigInteger(number); | |
_scale = BitConverter.ToInt32(flags, 0); | |
} | |
public BigDecimal(BigRational value) | |
{ | |
var num = (BigDecimal)value.Numerator; | |
var den = (BigDecimal)value.Denominator; | |
var bigDecRes = num / den; | |
_unscaledValue = bigDecRes._unscaledValue; | |
_scale = bigDecRes.Scale; | |
} | |
public BigDecimal(BigDecimal value) | |
{ | |
_unscaledValue = value._unscaledValue; | |
_scale = value.Scale; | |
} | |
public BigDecimal(decimal value) | |
{ | |
var bytes = new byte[16]; | |
var bits = decimal.GetBits(value); | |
var lo = bits[0]; | |
var mid = bits[1]; | |
var hi = bits[2]; | |
var flags = bits[3]; | |
bytes[0] = (byte)lo; | |
bytes[1] = (byte)(lo >> 8); | |
bytes[2] = (byte)(lo >> 0x10); | |
bytes[3] = (byte)(lo >> 0x18); | |
bytes[4] = (byte)mid; | |
bytes[5] = (byte)(mid >> 8); | |
bytes[6] = (byte)(mid >> 0x10); | |
bytes[7] = (byte)(mid >> 0x18); | |
bytes[8] = (byte)hi; | |
bytes[9] = (byte)(hi >> 8); | |
bytes[10] = (byte)(hi >> 0x10); | |
bytes[11] = (byte)(hi >> 0x18); | |
bytes[12] = (byte)flags; | |
bytes[13] = (byte)(flags >> 8); | |
bytes[14] = (byte)(flags >> 0x10); | |
bytes[15] = (byte)(flags >> 0x18); | |
var unscaledValueBytes = new byte[12]; | |
Array.Copy(bytes, unscaledValueBytes, unscaledValueBytes.Length); | |
var unscaledValue = new BigInteger(unscaledValueBytes); | |
var scale = bytes[14]; | |
if (bytes[15] == 128) | |
unscaledValue *= BigInteger.MinusOne; | |
_unscaledValue = unscaledValue; | |
_scale = scale; | |
} | |
public BigDecimal(string value) | |
{ | |
if (!value.ContainsOnly("0123456789+-.eE")) | |
throw new Exception($"Input value must only contain these '0123456789+-.eE', value'{value}"); | |
var len = value.Length; | |
var start = 0; | |
var point = 0; | |
var dot = -1; | |
var negative = false; | |
if (value[0] == '+') | |
{ | |
++start; | |
++point; | |
} | |
else if (value[0] == '-') | |
{ | |
++start; | |
++point; | |
negative = true; | |
} | |
while (point < len) | |
{ | |
var c = value[point]; | |
if (c == '.') | |
{ | |
if (dot >= 0) | |
throw new Exception($"There are multiple '.'s in the value {value}"); | |
dot = point; | |
} | |
else if (c == 'e' || c == 'E') | |
{ | |
break; | |
} | |
++point; | |
} | |
string val; | |
if (dot >= 0) | |
{ | |
val = value.Substring(start, dot) + value.Substring(dot + 1, point - (dot + 1)); | |
_scale = point - 1 - dot; | |
} | |
else | |
{ | |
val = value.Substring(start, point); | |
_scale = 0; | |
} | |
if (val.Length == 0) | |
throw new Exception($"There are no digits in the value {value}"); | |
if (negative) | |
val = "-" + val; | |
_unscaledValue = val.BigIntegerBase10(); | |
if (point < len) | |
try | |
{ | |
point++; | |
switch (value[point]) | |
{ | |
case '+': | |
{ | |
point++; | |
if (point >= len) | |
throw new Exception($"No exponent following e or E. Value {value}"); | |
var scale = value.Substring(point); | |
_scale -= int.Parse(scale); | |
return; | |
} | |
case '-': | |
{ | |
point++; | |
if (point >= len) | |
throw new Exception($"No exponent following e or E. Value {value}"); | |
var scale = value.Substring(point); | |
_scale += int.Parse(scale); | |
return; | |
} | |
default: | |
throw new Exception($"Malformed exponent in value {value}"); | |
} | |
} | |
catch (Exception ex) | |
{ | |
throw new Exception($"Malformed exponent in value {value}"); | |
} | |
} | |
public int BitWidth | |
{ | |
get => _bitWidth; | |
set | |
{ | |
_bitWidth = value; | |
MaxPrecision = _bitWidth / 64f * 20f; | |
} | |
} | |
private string DDisplay => ToString(); | |
public static BigDecimal Zero | |
{ | |
get; | |
} = new(BigInteger.Zero); | |
public static BigDecimal One | |
{ | |
get; | |
} = new(BigInteger.One); | |
public static BigDecimal MinusOne | |
{ | |
get; | |
} = new(BigInteger.MinusOne); | |
public bool IsEven => _unscaledValue.IsEven; | |
public bool IsOne => _unscaledValue.IsOne; | |
public bool IsPowerOfTwo => _unscaledValue.IsPowerOfTwo; | |
public bool IsZero => _unscaledValue.IsZero; | |
public int Sign => _unscaledValue.Sign; | |
public int Scale | |
{ | |
get => _scale; | |
private set => _scale = value; | |
} | |
public BigInteger UnscaledValue => _unscaledValue; | |
public BigDecimal WholePart => BigInteger.Divide(_unscaledValue, BigInteger.Pow(10, Scale)); | |
public BigDecimal FractionalPart => this - WholePart; | |
public int DecimalPlaces | |
{ | |
get | |
{ | |
var a = new BigDecimal(_unscaledValue); | |
var dPlaces = 0; | |
if (a.Sign == 0) | |
return 1; | |
if (a.Sign < 0) | |
try | |
{ | |
a = -a; | |
} | |
catch (Exception ex) | |
{ | |
return 0; | |
} | |
var biRadix = new BigDecimal(10); | |
while (a > 0) | |
try | |
{ | |
a /= biRadix; | |
dPlaces++; | |
} | |
catch (Exception ex) | |
{ | |
break; | |
} | |
return dPlaces; | |
} | |
} | |
int IComparable.CompareTo(object obj) | |
{ | |
if (obj == null) | |
return 1; | |
if (!(obj is BigDecimal)) | |
throw new Exception("Argument must be of type BigDecimal."); | |
return Compare(this, (BigDecimal)obj); | |
} | |
public int CompareTo(BigDecimal other) | |
{ | |
return Compare(this, other); | |
} | |
public bool Equals(BigDecimal other) | |
{ | |
return _unscaledValue == other._unscaledValue && _scale == other.Scale; | |
} | |
public override bool Equals(object obj) | |
{ | |
if (obj == null) | |
return false; | |
if (!(obj is BigDecimal)) | |
return false; | |
return Equals((BigDecimal)obj); | |
} | |
public static BigDecimal Round(BigDecimal number, int decimalPlaces) | |
{ | |
BigDecimal power = BigInteger.Pow(10, decimalPlaces); | |
number *= power; | |
return number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power; | |
} | |
public void Round(int decimalPlaces) | |
{ | |
var number = this; | |
BigDecimal power = BigInteger.Pow(10, decimalPlaces); | |
number *= power; | |
var n = number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power; | |
_unscaledValue = n._unscaledValue; | |
_scale = n.Scale; | |
} | |
public override int GetHashCode() | |
{ | |
return UnscaledValue.GetHashCode() ^ _scale.GetHashCode(); | |
} | |
public override string ToString() | |
{ | |
return ToStringInt(); | |
} | |
public string ToString(IFormatProvider provider) | |
{ | |
var number = _unscaledValue.ToString("G"); | |
if (_scale > 0 && WholePart != 0 && number.Length - _scale >= 0) | |
return number.Insert(number.Length - _scale, "."); | |
return _unscaledValue.ToString(provider); | |
} | |
private string ToStringInt() | |
{ | |
var number = _unscaledValue.ToString("G"); | |
if (_scale > 0 && WholePart != 0 && number.Length - _scale >= 0) | |
return number.Insert(number.Length - _scale, "."); | |
StringBuilder buf; | |
var intString = _unscaledValue.ToString(); | |
var insertionPoint = intString.Length - _scale; | |
if (insertionPoint == 0) | |
return (Sign < 0 ? "-0." : "0.") + intString; | |
if (insertionPoint > 0) | |
{ | |
buf = new StringBuilder(intString); | |
buf.Insert(insertionPoint, '.'); | |
if (Sign < 0) | |
buf.Insert(0, '-'); | |
} | |
else | |
{ | |
buf = new StringBuilder(3 - insertionPoint + intString.Length); | |
buf.Append(Sign < 0 ? "-0." : "0."); | |
for (var i = 0; i < -insertionPoint; i++) | |
buf.Append('0'); | |
buf.Append(intString); | |
} | |
if (_scale == 0) | |
buf.Append("0"); | |
return buf.ToString(); | |
} | |
public static BigDecimal Parse(string value) | |
{ | |
return new BigDecimal(value); | |
} | |
public byte[] ToByteArray() | |
{ | |
var unscaledValue = _unscaledValue.ToByteArray(); | |
var scale = BitConverter.GetBytes(_scale); | |
var bytes = new byte[unscaledValue.Length + scale.Length]; | |
Array.Copy(unscaledValue, 0, bytes, 0, unscaledValue.Length); | |
Array.Copy(scale, 0, bytes, unscaledValue.Length, scale.Length); | |
return bytes; | |
} | |
public (byte[] unscaledValue, byte[] scale) ToByteArrays() | |
{ | |
return (_unscaledValue.ToByteArray(), BitConverter.GetBytes(_scale)); | |
} | |
public static BigDecimal Abs(BigDecimal value) | |
{ | |
return value._unscaledValue.Sign < 0 ? -value : value; | |
} | |
public static BigDecimal Negate(BigDecimal value) | |
{ | |
return new BigDecimal(BigInteger.Negate(value._unscaledValue), value.Scale); | |
} | |
public static BigDecimal Add(BigDecimal x, BigDecimal y) | |
{ | |
return x + y; | |
} | |
public static BigDecimal Subtract(BigDecimal x, BigDecimal y) | |
{ | |
return x - y; | |
} | |
public static BigDecimal Multiply(BigDecimal x, BigDecimal y) | |
{ | |
return x * y; | |
} | |
public static BigDecimal Divide(BigDecimal dividend, BigDecimal divisor) | |
{ | |
return dividend / divisor; | |
} | |
public static BigDecimal Pow(BigDecimal baseValue, BigInteger exponent) | |
{ | |
if (exponent.Sign == 0) | |
return One; | |
if (exponent.Sign < 0) | |
{ | |
if (baseValue == Zero) | |
throw new Exception("Cannot raise zero to a negative power."); | |
baseValue = One / baseValue; | |
exponent = BigInteger.Negate(exponent); | |
} | |
var result = baseValue; | |
while (exponent > BigInteger.One) | |
{ | |
result *= baseValue; | |
exponent--; | |
} | |
return result; | |
} | |
public static int Compare(BigDecimal r1, BigDecimal r2) | |
{ | |
return (r1 - r2)._unscaledValue.Sign; | |
} | |
public static bool operator ==(BigDecimal x, BigDecimal y) | |
{ | |
return x.Equals(y); | |
} | |
public static bool operator !=(BigDecimal x, BigDecimal y) | |
{ | |
return !x.Equals(y); | |
} | |
public static bool operator <(BigDecimal x, BigDecimal y) | |
{ | |
return Compare(x, y) < 0; | |
} | |
public static bool operator <=(BigDecimal x, BigDecimal y) | |
{ | |
return Compare(x, y) <= 0; | |
} | |
public static bool operator >(BigDecimal x, BigDecimal y) | |
{ | |
return Compare(x, y) > 0; | |
} | |
public static bool operator >=(BigDecimal x, BigDecimal y) | |
{ | |
return Compare(x, y) >= 0; | |
} | |
public static BigDecimal operator +(BigDecimal value) | |
{ | |
return value; | |
} | |
public static BigDecimal operator -(BigDecimal value) | |
{ | |
return new BigDecimal(-value._unscaledValue, value.Scale); | |
} | |
public static BigDecimal operator ++(BigDecimal value) | |
{ | |
return value + One; | |
} | |
public static BigDecimal operator --(BigDecimal value) | |
{ | |
return value - One; | |
} | |
public static BigDecimal operator +(BigDecimal left, BigDecimal right) | |
{ | |
BigDecimal ret; | |
if (left.Scale >= right.Scale) | |
{ | |
ret = left; | |
} | |
else | |
{ | |
var value = left._unscaledValue * BigInteger.Pow(10, right.Scale - left.Scale); | |
ret = new BigDecimal(value, right.Scale); | |
} | |
BigDecimal ret1; | |
if (right.Scale >= left.Scale) | |
{ | |
ret1 = right; | |
} | |
else | |
{ | |
var value1 = right._unscaledValue * BigInteger.Pow(10, left.Scale - right.Scale); | |
ret1 = new BigDecimal(value1, left.Scale); | |
} | |
return new BigDecimal(ret._unscaledValue + ret1._unscaledValue, ret.Scale); | |
} | |
public static BigDecimal operator -(BigDecimal left, BigDecimal right) | |
{ | |
BigDecimal ret; | |
if (left.Scale >= right.Scale) | |
{ | |
ret = left; | |
} | |
else | |
{ | |
var value = left._unscaledValue * BigInteger.Pow(10, right.Scale - left.Scale); | |
ret = new BigDecimal(value, right.Scale); | |
} | |
BigDecimal ret1; | |
if (right.Scale >= left.Scale) | |
{ | |
ret1 = right; | |
} | |
else | |
{ | |
var value1 = right._unscaledValue * BigInteger.Pow(10, left.Scale - right.Scale); | |
ret1 = new BigDecimal(value1, left.Scale); | |
} | |
return new BigDecimal(ret._unscaledValue - ret1._unscaledValue, ret.Scale); | |
} | |
public static BigDecimal operator *(BigDecimal left, BigDecimal right) | |
{ | |
return new BigDecimal(left._unscaledValue * right._unscaledValue, left.Scale + right.Scale); | |
} | |
public static BigDecimal operator /(BigDecimal left, BigDecimal right) | |
{ | |
var value = left._unscaledValue; | |
var scale = left.Scale; | |
while (scale > 0 && value % 10 == 0) | |
{ | |
value /= 10; | |
scale--; | |
} | |
var v1 = new BigDecimal(value, scale); | |
var value1 = right._unscaledValue; | |
var scale1 = right.Scale; | |
while (scale1 > 0 && value1 % 10 == 0) | |
{ | |
value1 /= 10; | |
scale1--; | |
} | |
var v2 = new BigDecimal(value1, scale1); | |
while (v1.Scale > 0 || v2.Scale > 0) | |
{ | |
if (v1.Scale > 0) | |
v1 = new BigDecimal(v1._unscaledValue, v1.Scale - 1); | |
else | |
v1 = new BigDecimal(v1._unscaledValue * 10, 0); | |
if (v2.Scale > 0) | |
v2 = new BigDecimal(v2._unscaledValue, v2.Scale - 1); | |
else | |
v2 = new BigDecimal(v2._unscaledValue * 10, 0); | |
} | |
var factor = 0; | |
var v1Value = v1._unscaledValue; | |
while (factor < (int)MaxPrecision && v1Value % v2._unscaledValue != 0) | |
{ | |
v1Value *= 10; | |
factor++; | |
} | |
return new BigDecimal(v1Value / v2._unscaledValue, factor); | |
} | |
public static BigDecimal operator %(BigDecimal left, BigDecimal right) | |
{ | |
var value = left._unscaledValue; | |
var scale = left.Scale; | |
while (scale > 0 && value % 10 == 0) | |
{ | |
value /= 10; | |
scale--; | |
} | |
var v1 = new BigDecimal(value, scale); | |
var value1 = right._unscaledValue; | |
var scale1 = right.Scale; | |
while (scale1 > 0 && value1 % 10 == 0) | |
{ | |
value1 /= 10; | |
scale1--; | |
} | |
var v2 = new BigDecimal(value1, scale1); | |
while (v1.Scale > 0 || v2.Scale > 0) | |
{ | |
if (v1.Scale > 0) | |
v1 = new BigDecimal(v1._unscaledValue, v1.Scale - 1); | |
else | |
v1 = new BigDecimal(v1._unscaledValue * 10, 0); | |
if (v2.Scale > 0) | |
v2 = new BigDecimal(v2._unscaledValue, v2.Scale - 1); | |
else | |
v2 = new BigDecimal(v2._unscaledValue * 10, 0); | |
} | |
return new BigDecimal(v1._unscaledValue % v2._unscaledValue); | |
} | |
public static explicit operator sbyte(BigDecimal value) | |
{ | |
return (sbyte)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); | |
} | |
public static explicit operator ushort(BigDecimal value) | |
{ | |
return (ushort)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); | |
} | |
public static explicit operator uint(BigDecimal value) | |
{ | |
return (uint)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); | |
} | |
public static explicit operator ulong(BigDecimal value) | |
{ | |
return (ulong)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); | |
} | |
public static explicit operator byte(BigDecimal value) | |
{ | |
return (byte)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); | |
} | |
public static explicit operator short(BigDecimal value) | |
{ | |
return (short)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); | |
} | |
public static explicit operator int(BigDecimal value) | |
{ | |
return (int)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); | |
} | |
public static explicit operator long(BigDecimal value) | |
{ | |
return (long)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); | |
} | |
public static explicit operator BigInteger(BigDecimal value) | |
{ | |
return BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); | |
} | |
public static explicit operator xIntX(BigDecimal value) | |
{ | |
return xIntX.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); | |
} | |
public static explicit operator BigIntX(BigDecimal value) | |
{ | |
return BigIntX.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); | |
} | |
public static explicit operator float(BigDecimal value) | |
{ | |
return (float)(double)value; | |
} | |
public static explicit operator double(BigDecimal value) | |
{ | |
var factor = BigInteger.Pow(10, value.Scale); | |
if (SafeCastToDouble(value._unscaledValue) && SafeCastToDouble(factor)) | |
return (double)value._unscaledValue / (double)factor; | |
var dnv = value._unscaledValue * DoublePrecision / factor; | |
if (dnv.IsZero) | |
return value.Sign < 0 ? BitConverter.Int64BitsToDouble(unchecked((long)0x8000000000000000)) : 0d; | |
double result = 0; | |
var isDouble = false; | |
var scale = 308; | |
while (scale > 0) | |
{ | |
if (!isDouble) | |
{ | |
if (SafeCastToDouble(dnv)) | |
{ | |
result = (double)dnv; | |
isDouble = true; | |
} | |
else | |
{ | |
dnv /= 10; | |
} | |
} | |
result /= 10; | |
scale--; | |
} | |
if (!isDouble) | |
return value.Sign < 0 ? double.NegativeInfinity : double.PositiveInfinity; | |
return result; | |
} | |
public static explicit operator decimal(BigDecimal value) | |
{ | |
var factor = BigInteger.Pow(10, value.Scale); | |
if (SafeCastToDecimal(value._unscaledValue) && SafeCastToDecimal(factor)) | |
return (decimal)value._unscaledValue / (decimal)factor; | |
var dnv = value._unscaledValue * DecimalPrecision / factor; | |
if (dnv.IsZero) | |
return decimal.Zero; | |
for (var scale = 28; scale >= 0; scale--) | |
if (!SafeCastToDecimal(dnv)) | |
{ | |
dnv /= 10; | |
} | |
else | |
{ | |
var dec = new DecimalUInt32(); | |
dec.dec = (decimal)dnv; | |
dec.flags = (dec.flags & ~0x00FF0000) | (scale << 16); | |
return dec.dec; | |
} | |
throw new Exception("Value was either too large or too small for a Decimal."); | |
} | |
public static implicit operator BigDecimal(sbyte value) | |
{ | |
return new BigDecimal(value); | |
} | |
public static implicit operator BigDecimal(ushort value) | |
{ | |
return new BigDecimal(value); | |
} | |
public static implicit operator BigDecimal(uint value) | |
{ | |
return new BigDecimal(value); | |
} | |
public static implicit operator BigDecimal(ulong value) | |
{ | |
return new BigDecimal((BigInteger)value); | |
} | |
public static implicit operator BigDecimal(byte value) | |
{ | |
return new BigDecimal(value); | |
} | |
public static implicit operator BigDecimal(short value) | |
{ | |
return new BigDecimal(value); | |
} | |
public static implicit operator BigDecimal(int value) | |
{ | |
return new BigDecimal(value); | |
} | |
public static implicit operator BigDecimal(long value) | |
{ | |
return new BigDecimal(value); | |
} | |
public static implicit operator BigDecimal(BigInteger value) | |
{ | |
return new BigDecimal(value); | |
} | |
public static implicit operator BigDecimal(xIntX value) | |
{ | |
return new BigDecimal(value); | |
} | |
public static implicit operator BigDecimal(BigIntX value) | |
{ | |
return new BigDecimal(value); | |
} | |
public static implicit operator BigDecimal(float value) | |
{ | |
return new BigDecimal(value); | |
} | |
public static implicit operator BigDecimal(double value) | |
{ | |
return new BigDecimal(value); | |
} | |
public static implicit operator BigDecimal(decimal value) | |
{ | |
return new BigDecimal(value); | |
} | |
public static implicit operator BigDecimal(BigRational value) | |
{ | |
return new BigDecimal(value); | |
} | |
private static bool SafeCastToDouble(BigInteger value) | |
{ | |
return DoubleMinValue <= value && value <= DoubleMaxValue; | |
} | |
private static bool SafeCastToDecimal(BigInteger value) | |
{ | |
return DecimalMinValue <= value && value <= DecimalMaxValue; | |
} | |
private static BigInteger GetLastDigit(BigInteger value) | |
{ | |
return value % new BigInteger(10); | |
} | |
private static int GetNumberOfDigits(BigInteger value) | |
{ | |
return BigInteger.Abs(value).ToString().Length; | |
} | |
private static BigDecimal Factorial(BigDecimal x) | |
{ | |
BigDecimal r = 1; | |
BigDecimal c = 1; | |
while (c <= x) | |
{ | |
r *= c; | |
c++; | |
} | |
return r; | |
} | |
public static BigDecimal Exp(BigDecimal x) | |
{ | |
BigDecimal r = 0; | |
BigDecimal r1 = 0; | |
var k = 0; | |
while (true) | |
{ | |
r += Pow(x, k) / Factorial(k); | |
if (r == r1) | |
break; | |
r1 = r; | |
k++; | |
} | |
return r; | |
} | |
public static BigDecimal Sine(BigDecimal ar, int n) | |
{ | |
if (Factorials == null) | |
{ | |
Factorials = new BigDecimal[MaxFactorials]; | |
for (var i = 0; i < MaxFactorials; i++) | |
Factorials[i] = new BigDecimal(0, 0); | |
for (var i = 1; i < MaxFactorials + 1; i++) | |
Factorials[i - 1] = Factorial(i); | |
} | |
var sin = ar; | |
for (var i = 1; i <= n; i++) | |
{ | |
var trm = Pow(ar, i * 2 + 1); | |
trm /= Factorials[i * 2]; | |
if ((i & 1) == 1) | |
sin -= trm; | |
else | |
sin += trm; | |
} | |
return sin; | |
} | |
public static BigDecimal Atan(BigDecimal ar, int n) | |
{ | |
var atan = ar; | |
for (var i = 1; i <= n; i++) | |
{ | |
var trm = Pow(ar, i * 2 + 1); | |
trm /= i * 2; | |
if ((i & 1) == 1) | |
atan -= trm; | |
else | |
atan += trm; | |
} | |
return atan; | |
} | |
public static BigDecimal Cosine(BigDecimal ar, int n) | |
{ | |
if (Factorials == null) | |
{ | |
Factorials = new BigDecimal[MaxFactorials]; | |
for (var i = 0; i < MaxFactorials; i++) | |
Factorials[i] = new BigDecimal(0, 0); | |
for (var i = 1; i < MaxFactorials + 1; i++) | |
Factorials[i - 1] = Factorial(i); | |
} | |
BigDecimal cos = 1.0; | |
for (var i = 1; i <= n; i++) | |
{ | |
var trm = Pow(ar, i * 2); | |
trm /= Factorials[i * 2 - 1]; | |
if ((i & 1) == 1) | |
cos -= trm; | |
else | |
cos += trm; | |
} | |
return cos; | |
} | |
public static BigDecimal GetE(int n) | |
{ | |
BigDecimal e = 1.0; | |
var c = n; | |
while (c > 0) | |
{ | |
BigDecimal f = 0; | |
if (c == 1) | |
{ | |
f = 1; | |
} | |
else | |
{ | |
var i = c - 1; | |
f = c; | |
while (i > 0) | |
{ | |
f *= i; | |
i--; | |
} | |
} | |
c--; | |
e += 1.0 / f; | |
} | |
return e; | |
} | |
public static BigDecimal Tangent(BigDecimal ar, int n) | |
{ | |
return Sine(ar, n) / Cosine(ar, n); | |
} | |
public static BigDecimal CoTangent(BigDecimal ar, int n) | |
{ | |
return Cosine(ar, n) / Sine(ar, n); | |
} | |
public static BigDecimal Secant(BigDecimal ar, int n) | |
{ | |
return 1.0 / Cosine(ar, n); | |
} | |
public static BigDecimal NthRoot(BigDecimal value, int nth) | |
{ | |
BigDecimal lx; | |
var a = value; | |
var n = nth; | |
BigDecimal s = 1.0; | |
do | |
{ | |
var t = s; | |
lx = a / Pow(s, n - 1); | |
var r = (n - 1) * s; | |
s = (lx + r) / n; | |
} while (lx != s); | |
return s; | |
} | |
public static BigDecimal LogN(BigDecimal value) | |
{ | |
var E = GetE(MaxFactorials); | |
BigDecimal a; | |
var p = value; | |
BigDecimal n = 0.0; | |
while (p >= E) | |
{ | |
p /= E; | |
n++; | |
} | |
n += p / E; | |
p = value; | |
do | |
{ | |
a = n; | |
var lx = p / Exp(n - 1.0); | |
var r = (n - 1.0) * E; | |
n = (lx + r) / E; | |
} while (n != a); | |
return n; | |
} | |
public static BigDecimal Log(BigDecimal n, int b) | |
{ | |
return LogN(n) / LogN(b); | |
} | |
public static BigDecimal CoSecant(BigDecimal ar, int n) | |
{ | |
return 1.0 / Sine(ar, n); | |
} | |
public static BigDecimal Ceiling(BigDecimal value, int precision) | |
{ | |
var v1 = new BigDecimal(value); | |
v1 = RemoveTrailingZeros(v1); | |
var diff = GetNumberOfDigits(v1._unscaledValue) - precision; | |
if (diff > 0) | |
{ | |
for (var i = 0; i < diff; i++) | |
{ | |
v1._unscaledValue = BigInteger.Divide(v1._unscaledValue, 10); | |
v1.Scale--; | |
} | |
if (v1._unscaledValue.Sign < 0) | |
v1._unscaledValue--; | |
else | |
v1._unscaledValue++; | |
} | |
return v1; | |
} | |
public static BigDecimal Floor(BigDecimal value, int precision) | |
{ | |
var v1 = new BigDecimal(value); | |
v1 = RemoveTrailingZeros(v1); | |
var diff = GetNumberOfDigits(v1._unscaledValue) - precision; | |
if (diff > 0) | |
{ | |
for (var i = 0; i < diff; i++) | |
{ | |
v1._unscaledValue = BigInteger.Divide(v1._unscaledValue, 10); | |
v1.Scale--; | |
} | |
if (v1._unscaledValue.Sign > 0) | |
v1._unscaledValue--; | |
else | |
v1._unscaledValue++; | |
} | |
return v1; | |
} | |
private static BigDecimal RemoveTrailingZeros(BigDecimal value) | |
{ | |
var v1 = new BigDecimal(value); | |
BigInteger remainder; | |
do | |
{ | |
var shortened = BigInteger.DivRem(v1._unscaledValue, 10, out remainder); | |
if (remainder == BigInteger.Zero) | |
{ | |
v1._unscaledValue = shortened; | |
v1.Scale--; | |
} | |
} while (remainder == BigInteger.Zero); | |
return v1; | |
} | |
public BigDecimal Min(BigDecimal value) | |
{ | |
return CompareTo(value) <= 0 ? this : value; | |
} | |
public BigDecimal Max(BigDecimal value) | |
{ | |
return CompareTo(value) >= 0 ? this : value; | |
} | |
public static BigDecimal Sqrt(BigDecimal value) | |
{ | |
return BigRational.Sqrt(value); | |
} | |
[StructLayout(LayoutKind.Explicit)] | |
internal struct DecimalUInt32 | |
{ | |
[FieldOffset(0)] public decimal dec; | |
[FieldOffset(0)] public int flags; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment