I recently came across a need to display very large decimal numbers and realized that I could use a BigDecimal class. After some tinkering I decided to leverage the BigInteger
class to do the heavy lifting.
using System;
using System.Numerics;
using System.Text;
namespace Numerics
{
public class BigDecimal : IComparable
{
//The number represents all the digits in the displayed value.
//The precision is the number of decimal places.
//maxPrecision is the maximum number of decimal places allowed.
//This is adjustable to any arbitrary number with the function provided.
BigInteger number = 0;
BigInteger precision = 0;
static BigInteger maxPrecision = 20;
public static BigInteger MAXPRECISION
{
get { return maxPrecision; }
}
#region Constructors
//Constructors that accept limited numeric types
public BigDecimal()
{
BigInteger test = new BigInteger();
BigInteger test2 = test + 1;
}
public BigDecimal(BigInteger num)
{
number = num;
}
public BigDecimal(int num)
{
number = num;
}
public BigDecimal(double num)
{
BigDecimal temp = new BigDecimal((decimal)num);
number = temp.number;
precision = temp.precision;
}
public BigDecimal(decimal num)
{
number = (BigInteger)num;
if (num == 0)
{
return;
}
if (number == 0)
{
string tempNum = num.ToString();
if (num < 0)
{
tempNum = "-" + tempNum.Substring(tempNum.IndexOf('.') + 1);
}
tempNum = tempNum.Substring(tempNum.IndexOf('.') + 1);
precision = tempNum.Length;
number = BigInteger.Parse(tempNum);
return;
}
string temp = decimal.Remainder(num, decimal.Truncate(num)).ToString();
string tempFraction = temp.Substring(temp.IndexOf('.') + 1);
precision = tempFraction.Length;
number = BigInteger.Parse(tempFraction) + (number * BIPow(10, precision));
}
public BigDecimal(float num)
{
BigDecimal temp = new BigDecimal((decimal)num);
number = temp.number;
precision = temp.precision;
}
public BigDecimal(long num)
{
number = num;
}
public BigDecimal(ulong num)
{
number = num;
}
public BigDecimal(uint num)
{
number = num;
}
BigDecimal(byte[] num)
{
number = new BigInteger(num);
}
#endregion
#region Operators
//Implicit operators for simple casting from limited numeric types
public static implicit operator BigDecimal(int v)
{
return new BigDecimal(v);
}
public static implicit operator BigDecimal(double v)
{
return new BigDecimal(v);
}
public static implicit operator BigDecimal(decimal v)
{
return new BigDecimal(v);
}
public static implicit operator BigDecimal(float v)
{
return new BigDecimal(v);
}
public static implicit operator BigDecimal(BigInteger v)
{
return new BigDecimal(v);
}
public static implicit operator BigDecimal(long v)
{
return new BigDecimal(v);
}
public static implicit operator BigDecimal(ulong v)
{
return new BigDecimal(v);
}
public static implicit operator BigDecimal(uint v)
{
return new BigDecimal(v);
}
public static implicit operator BigDecimal(byte[] v)
{
return new BigDecimal(v);
}
//Equals operator
public static bool operator ==(BigDecimal a, BigDecimal b)
{
return a.CompareTo(b) == 0;
}
//Not Equals operator
public static bool operator !=(BigDecimal a, BigDecimal b)
{
return a.CompareTo(b) != 0;
}
//Plus operator
public static BigDecimal operator +(BigDecimal a, BigDecimal b)
{
BigDecimal outVal = 0;
BigInteger maxPrecision = BigInteger.Max(a.precision, b.precision);
if (a.precision < maxPrecision)
{
outVal = a.number * BIPow(10, maxPrecision - a.precision) + b.number;
}
if (b.precision < maxPrecision)
{
outVal = b.number * BIPow(10, maxPrecision - b.precision) + a.number;
}
outVal.precision = maxPrecision;
return outVal;
}
//Minus operator
public static BigDecimal operator -(BigDecimal a, BigDecimal b)
{
return a + (b * -1);
}
//Multiplication operator
public static BigDecimal operator *(BigDecimal a, BigDecimal b)
{
BigDecimal outVal = a.number * b.number;
outVal.precision = a.precision + b.precision;
return outVal;
}
//Division operator
public static BigDecimal operator /(BigDecimal a, BigDecimal b)
{
BigDecimal outVal = 0;
BigInteger dividend = a.number;
BigInteger divisor = b.number;
BigInteger maxPrecision = BigInteger.Max(a.precision, b.precision);
if (a.precision < maxPrecision)
{
a.precision = maxPrecision;
a.number = a.number * BIPow(10, maxPrecision - a.precision);
}
if (b.precision < maxPrecision)
{
b.precision = maxPrecision;
b.number = b.number * BIPow(10, maxPrecision - b.precision);
}
BigInteger remainder = 0;
outVal.number = BigInteger.DivRem(a.number, b.number, out remainder);
while (remainder != 0 && outVal.precision < MAXPRECISION)
{
while(BigInteger.Abs(remainder) < BigInteger.Abs(b.number))
{
remainder *= 10;
outVal.number *= 10;
outVal.precision++;
}
outVal.number = outVal.number + BigInteger.DivRem(remainder, b.number, out remainder);
}
return outVal;
}
//Greater than operator
public static bool operator >(BigDecimal a, BigDecimal b)
{
BigInteger maxPrecision = BigInteger.Max(a.precision, b.precision);
if (a.precision < maxPrecision)
{
return (a.number * BIPow(10, maxPrecision - a.precision)) > b.number;
}
if (b.precision < maxPrecision)
{
return a.number > (b.number * BIPow(10, maxPrecision - b.precision));
}
return a.number > b.number;
}
//Less than operator
public static bool operator <(BigDecimal a, BigDecimal b)
{
BigInteger maxPrecision = BigInteger.Max(a.precision, b.precision);
if (a.precision < maxPrecision)
{
return (a.number * BIPow(10, maxPrecision - a.precision)) < b.number;
}
if (b.precision < maxPrecision)
{
return a.number < (b.number * BIPow(10, maxPrecision - b.precision));
}
return a.number < b.number;
}
#endregion
#region Public Functions
public override string ToString()
{
String outVal = number.ToString();
if (precision == 0)
{
return string.Format("{0}.0", outVal);
}
string startString = "0.";
if (outVal.TrimStart("-".ToCharArray()).Length < precision)
{
if(number < 0)
{
startString = "-0.";
outVal = outVal.TrimStart("-".ToCharArray());
}
StringBuilder sb = new StringBuilder(startString + NewString('0', precision - outVal.Length) + outVal);
return sb.ToString();
}
return outVal.Insert(outVal.Length - (int)precision, ".");
}
public int CompareTo(object a)
{
return ToString().CompareTo(a.ToString());
}
public override int GetHashCode()
{
return ToString().GetHashCode();
}
public override bool Equals(object obj)
{
return this.CompareTo(obj) == 0;
}
//Function to change the maximum precision the class will use
public static void ChangeMaxPrecision(BigInteger value)
{
maxPrecision = value;
}
#endregion
#region Private Functions
//A function to build a new string of repeating characters using BigInteger count.
static string NewString(char c, BigInteger count)
{
if(count <= int.MaxValue)
{
return new string(c, (int)count);
}
StringBuilder sb = new StringBuilder();
for (BigInteger i = 0; i < count; i++)
{
sb.Append(c);
}
return sb.ToString();
}
//A function to calculate a number raised to a power both numbers represented as BigIntegers
static BigInteger BIPow(BigInteger input, BigInteger exp)
{
if (exp == 0)
{
return 1;
}
if (exp == 1)
{
return input;
}
BigInteger outval = 1;
while (exp != 0)
{
if (exp % 2 == 1)
{
outval *= input;
}
exp >>= 1;
input *= input;
}
return outval;
}
#endregion
}
}
So far I've only implemented the 4 basic math operations. I thought I would get some feedback on what I have so far before I implement more complex operations.
Updated plus operator to fix bug:
//Plus operator
public static BigDecimal operator +(BigDecimal a, BigDecimal b)
{
BigDecimal outVal = 0;
if (a.precision == b.precision)
{
outVal = a.number + b.number;
outVal.precision = a.precision;
return outVal;
}
BigInteger maxPrecision = BigInteger.Max(a.precision, b.precision);
if (a.precision < maxPrecision)
{
outVal = a.number * BIPow(10, maxPrecision - a.precision) + b.number;
}
else
{
outVal = b.number * BIPow(10, maxPrecision - b.precision) + a.number;
}
outVal.precision = maxPrecision;
return outVal;