2015-01-02 81 views
1

我目前正在編寫一個可以表示無限大數量的類(理論上)。該類的構造函數根據字符串值創建對象,這就是爲什麼該數字可能具有非常大但未知的大小。如何檢查表示數字的對象是否大於另一個對象?

我開始寫這門課的原因是因爲我希望能夠製作一個能夠用任意大小的數字進行數學計算的程序。於是,我開始寫一個能夠處理超過整數的標準範圍值的一類,花車,雙打,(希望)小數等

下面是聲明和該類的主要構造:

/// <summary> 
/// Creates a new instance of the LargeDecimal class, which represents either a whole or decimal number. 
/// </summary> 
/// <param name="number">The string representation of the number.</param> 
public LargeDecimal(string value) 
{ 
    string number = value.Replace(" ", ""); 
    if (number.Contains("-") && (number.IndexOf('-') == 0)) { 
     number = number.Replace("-", ""); 
     IsNegative = true; 
    } 
    // Determining whether the number is whole or contains a decimal. 
    if (number.IndexOf('.') == -1) { 
     // Does not contain a decimal. 
     for (int i = 0; i < number.Length; i++) 
      wholeDigits.Add(int.Parse(number[i].ToString())); 
     IsWhole = true; 
    } 
    else { 
     // Still check if number is whole. Add all decimal digits. 
     string[] numArray = number.Split('.'); 
     int sumOfDecimalDigits = 0; 
     for (int i = 0; i < numArray[1].ToString().Length; i++) 
      sumOfDecimalDigits += int.Parse(numArray[1].ToString()[i].ToString()); 
     if (sumOfDecimalDigits <= 0) { 
      // Is a whole number. 
      for (int i = 0; i < numArray[0].ToString().Length; i++) 
       wholeDigits.Add(int.Parse(numArray[0].ToString()[i].ToString())); 
      IsWhole = true; 
     } 
     else { 
      // Is not a whole number. 
      for (int i = 0; i < numArray[0].ToString().Length; i++) 
       wholeDigits.Add(int.Parse(numArray[0].ToString()[i].ToString())); 
      for (int i = 0; i < numArray[1].ToString().Length; i++) 
       decimalDigits.Add(int.Parse(numArray[1].ToString()[i].ToString())); 
      IsWhole = false; 
     } 
    } 
} 

該類基本上是通過兩個int類型的列表來表示一個數字,其中一個列表表示組成數字整個分區的數字,另一個列表表示構成該數字整數分區的數字號碼(如果適用)。

我寫了一個Add方法,它接受兩個LargeDecimal對象,將它們的值相加在一起,並返回一個新的LargeDecimal對象,其中sum爲值。雖然不完整,但它適用於只有整數的LargeDecimal對象,並且都是正數或負數(圖片!)。

我已經認識到,添加方法來比較兩個值(大於/小於/等於)將在計算中非常有用。但是,我不確定如何檢查LargeDecimal對象的值是大於還是小於另一個LargeDecimal的值。

有些情況下,我只能比較整個數字列表中的項目數量,但只有當兩個值的項目數量不同時。 我不知道如何把兩個數字比較,如:15498765423654973246和15499111137583924246.

而且我認爲它會變得更加困難,如果我會嘗試和比較兩個分數:8573819351.86931和8573809999.85999

我不希望結合地點值使用整數計算(例如,在數字831中,數字8的值將是8 * 100,3的值將是3 * 10,並且1的值將是1 * 1),因爲我希望這個類能夠表示任何給定大小和長度和範圍的值(而int不能處理高達2147483647的值)。

任何有關這方面的幫助將不勝感激!謝謝你們!

+3

或者你可以使用已經存在的東西:'BigInteger':http://msdn.microsoft.com/en-us/library/system.numerics.biginteger(v=vs.110).aspx – MarcinJuraszek

+0

我會使用BigIntegers,但我希望能夠使用小數部分的數字。 – Natsumi

+0

說真的,你將要處理的數字有多大? –

回答

2

我將通過實施IComparable開始:

public class LargeDecimal : IComparable<LargeDecimal> 

和實現會是什麼樣子:

public int CompareTo(LargeDecimal other) 
{ 
    if (other == null) return 1; 
    if (ReferenceEquals(this, other)) return 0; 

    if (IsNegative != other.IsNegative) 
    { 
     if (other.IsNegative) return 1; 
     return -1; 
    } 

    int multiplier = (IsNegative) ? -1 : 1; 

    if (wholeDigits.Count > other.wholeDigits.Count) return 1 * multiplier; 
    if (wholeDigits.Count < other.wholeDigits.Count) return -1 * multiplier; 

    for (int i = 0; i < wholeDigits.Count; i++) 
    { 
     if (wholeDigits[i] > other.wholeDigits[i]) return 1 * multiplier; 
     if (wholeDigits[i] < other.wholeDigits[i]) return -1 * multiplier; 
    } 

    for (int i = 0; i < Math.Min(decimalDigits.Count, other.decimalDigits.Count); i++) 
    { 
     if (decimalDigits[i] > other.decimalDigits[i]) return 1 * multiplier; 
     if (decimalDigits[i] < other.decimalDigits[i]) return -1 * multiplier; 
    } 

    if (decimalDigits.Count > other.decimalDigits.Count) return 1 * multiplier; 
    if (decimalDigits.Count < other.decimalDigits.Count) return -1 * multiplier; 

    return 0; 
} 

更新

該項目在晚飯坐在我的大腦今晚,所以我去了一些更有趣的。不知道這是否有幫助,但我想分享我想出來的東西。

首先,我添加的字段使課堂實際工作:

public bool IsNegative { get; private set; } 
public bool IsWhole { get; private set; } 

private List<int> wholeDigits; 
private List<int> decimalDigits; 

其次,我推翻了ToString方法,這樣的數字顯示很好:

public override string ToString() 
{ 
    return string.Format("{0}{1}{2}{3}", 
     (IsNegative) ? "-" : "", 
     string.Join("", wholeDigits), 
     (IsWhole) ? "" : ".", 
     (IsWhole) ? "" : string.Join("", decimalDigits)); 
} 

然後我實現了equals方法使他們按照預期的方式工作:

public static bool Equals(LargeDecimal first, LargeDecimal second) 
{ 
    return ReferenceEquals(first, null) 
     ? ReferenceEquals(second, null) 
     : first.Equals(second); 
} 

public override bool Equals(object obj) 
{ 
    return Equals(obj as LargeDecimal); 
} 

protected bool Equals(LargeDecimal other) 
{ 
    return CompareTo(other) == 0; 
} 

public override int GetHashCode() 
{ 
    unchecked 
    { 
     var hashCode = (wholeDigits != null) 
      ? wholeDigits.GetHashCode() 
      : 0; 
     hashCode = (hashCode * 397)^
      (decimalDigits != null ? decimalDigits.GetHashCode() : 0); 
     hashCode = (hashCode * 397)^IsNegative.GetHashCode(); 
     hashCode = (hashCode * 397)^IsWhole.GetHashCode(); 
     return hashCode; 
    } 
} 

Nex T,我增加了一些實用方法,以幫助一些即將到來的任務:

private void ResetToZero() 
{ 
    wholeDigits = new List<int> { 0 }; 
    decimalDigits = new List<int> { 0 }; 
    IsWhole = true; 
    IsNegative = false; 
} 

private void NormalizeLists() 
{ 
    RemoveLeadingZeroes(wholeDigits); 
    RemoveTrailingZeroes(decimalDigits); 
    IsWhole = (decimalDigits.Count == 0 
     || (decimalDigits.Count == 1 && decimalDigits[0] == 0)); 
} 

private void AddLeadingZeroes(List<int> list, int numberOfZeroes) 
{ 
    if (list == null) return; 

    for (int i = 0; i < numberOfZeroes; i++) 
    { 
     list.Insert(0, 0); 
    } 
} 

private void AddTrailingZeroes(List<int> list, int numberOfZeroes) 
{ 
    if (list == null) return; 

    for (int i = 0; i < numberOfZeroes; i++) 
    { 
     list.Add(0); 
    } 
} 

private void RemoveLeadingZeroes(List<int> list, bool leaveOneIfEmpty = true) 
{ 
    if (list == null) return; 

    var temp = list; 

    for (int i = 0; i < temp.Count; i++) 
    { 
     if (temp[i] == 0) 
     { 
      list.RemoveAt(i); 
     } 
     else 
     { 
      break; 
     } 
    } 

    if (leaveOneIfEmpty && !list.Any()) list.Add(0); 
} 

private void RemoveTrailingZeroes(List<int> list, bool leaveOneIfEmpty = true) 
{ 
    if (list == null) return; 

    var temp = list; 

    for (int i = temp.Count -1; i >= 0; i--) 
    { 
     if (temp[i] == 0) 
     { 
      list.RemoveAt(i); 
     } 
     else 
     { 
      break; 
     } 
    } 

    if (leaveOneIfEmpty && !list.Any()) list.Add(0); 
} 

接下來,我增加了一些構造函數。默認的那臺數是解析字符串「0」,一個,另一個從另一個LargeDecimal拷貝值:

public LargeDecimal() : this("0") { } 

public LargeDecimal(string value) 
{ 
    if (value == null) throw new ArgumentNullException("value"); 

    string number = value.Replace(" ", ""); // remove spaces 
    number = number.TrimStart('0'); // remove leading zeroes 
    IsNegative = (number.IndexOf('-') == 0); // check for negative 
    number = number.Replace("-", ""); // remove dashes 
    // add a zero if there were no numbers before a decimal point 
    if (number.IndexOf('.') == 0) number = "0" + number; 

    // Initialize lists 
    wholeDigits = new List<int>(); 
    decimalDigits = new List<int>(); 

    // Get whole and decimal parts of the number 
    var numberParts = number.Split(new[] {'.'}, 
     StringSplitOptions.RemoveEmptyEntries); 

    IsWhole = numberParts.Length == 1; 

    // Add whole digits to the list 
    wholeDigits.AddRange(numberParts[0].Select(n => int.Parse(n.ToString()))); 

    // Add decimal digits to the list (if there are any) 
    if (numberParts.Length > 1 && 
     numberParts[1].Sum(n => int.Parse(n.ToString())) > 0) 
    { 
     numberParts[1] = numberParts[1].TrimEnd('0'); 
     decimalDigits.AddRange(numberParts[1].Select(n => int.Parse(n.ToString()))); 
    } 

    NormalizeLists(); 
} 

public LargeDecimal(LargeDecimal initializeFrom) 
{ 
    wholeDigits = initializeFrom.wholeDigits 
     .GetRange(0, initializeFrom.wholeDigits.Count); 
    decimalDigits = initializeFrom.decimalDigits 
     .GetRange(0, initializeFrom.decimalDigits.Count); 
    IsWhole = initializeFrom.IsWhole; 
    IsNegative = initializeFrom.IsNegative; 
    NormalizeLists(); 
} 

然後我實現了加減法

public void Add(LargeDecimal other) 
{ 
    if (other == null) return; 

    if (IsNegative != other.IsNegative) 
    { 
     // Get the absolue values of the two operands 
     var absThis = new LargeDecimal(this) {IsNegative = false}; 
     var absOther = new LargeDecimal(other) {IsNegative = false}; 

     // If the signs are different and the values are the same, reset to 0. 
     if (absThis == absOther) 
     { 
      ResetToZero(); 
      return; 
     } 

     // Since the signs are different, we will retain the sign of the larger number 
     IsNegative = absThis < absOther ? other.IsNegative : IsNegative; 

     // Assign the difference of the two absolute values 
     absThis.Subtract(absOther); 
     wholeDigits = absThis.wholeDigits.GetRange(0, absThis.wholeDigits.Count); 
     decimalDigits = absThis.decimalDigits.GetRange(0, absThis.decimalDigits.Count); 
     NormalizeLists(); 
     return; 
    } 

    // start with the larger decimal digits list 
    var newDecimalDigits = new List<int>(); 
    newDecimalDigits = decimalDigits.Count > other.decimalDigits.Count 
     ? decimalDigits.GetRange(0, decimalDigits.Count) 
     : other.decimalDigits.GetRange(0, other.decimalDigits.Count); 

    // and add the smaller one to it 
    int carry = 0; // Represents the value of the 'tens' digit to carry over 
    for (int i = Math.Min(decimalDigits.Count, other.decimalDigits.Count) - 1; i >= 0; i--) 
    { 
     var result = decimalDigits[i] + other.decimalDigits[i] + carry; 
     carry = Convert.ToInt32(Math.Floor((decimal) result/10)); 
     result = result % 10; 
     newDecimalDigits[i] = result; 
    } 

    var newWholeDigits = new List<int>(); 
    newWholeDigits = wholeDigits.Count > other.wholeDigits.Count 
     ? wholeDigits.GetRange(0, wholeDigits.Count) 
     : other.wholeDigits.GetRange(0, other.wholeDigits.Count); 

    for (int i = Math.Min(wholeDigits.Count, other.wholeDigits.Count) - 1; i >= 0; i--) 
    { 
     var result = wholeDigits[i] + other.wholeDigits[i] + carry; 
     carry = Convert.ToInt32(Math.Floor((decimal)result/10)); 
     result = result % 10; 
     newWholeDigits[i] = result; 
    } 

    if (carry > 0) newWholeDigits.Insert(0, carry); 

    wholeDigits = newWholeDigits.GetRange(0, newWholeDigits.Count); 
    decimalDigits = newDecimalDigits.GetRange(0, newDecimalDigits.Count); 
    NormalizeLists(); 
} 

public void Subtract(LargeDecimal other) 
{ 
    if (other == null) return; 

    // If the other value is the same as this one, then the difference is zero 
    if (Equals(other)) 
    { 
     ResetToZero(); 
     return; 
    } 

    // Absolute values will be used to determine how we subtract 
    var absThis = new LargeDecimal(this) {IsNegative = false}; 
    var absOther = new LargeDecimal(other) {IsNegative = false}; 

    // If the signs are different, then the difference will be the sum 
    if (IsNegative != other.IsNegative) 
    { 
     absThis.Add(absOther); 
     wholeDigits = absThis.wholeDigits.GetRange(0, absThis.wholeDigits.Count); 
     decimalDigits = absThis.decimalDigits.GetRange(0, absThis.decimalDigits.Count); 
     NormalizeLists(); 
     return; 
    } 

    // Subtract smallNumber from bigNumber to get the difference 
    LargeDecimal bigNumber; 
    LargeDecimal smallNumber; 

    if (absThis < absOther) 
    { 
     bigNumber = new LargeDecimal(absOther); 
     smallNumber = new LargeDecimal(absThis); 
    } 
    else 
    { 
     bigNumber = new LargeDecimal(absThis); 
     smallNumber = new LargeDecimal(absOther); 
    } 

    // Pad the whole number and decimal number lists where necessary so that both 
    // LargeDecimal objects have the same count of whole and decimal numbers. 
    AddTrailingZeroes(
     bigNumber.decimalDigits.Count < smallNumber.decimalDigits.Count 
      ? bigNumber.decimalDigits 
      : smallNumber.decimalDigits, 
     Math.Abs(bigNumber.decimalDigits.Count - smallNumber.decimalDigits.Count)); 

    AddLeadingZeroes(smallNumber.wholeDigits, 
     Math.Abs(bigNumber.wholeDigits.Count - smallNumber.wholeDigits.Count)); 

    var newWholeDigits = new List<int>(); 
    var newDecimalDigits = new List<int>(); 

    bool borrowed = false; // True if we borrowed 1 from next number 
    for (int i = bigNumber.decimalDigits.Count - 1; i >= 0; i--) 
    { 
     if (borrowed) 
     { 
      bigNumber.decimalDigits[i] -= 1; // We borrowed one from this number last time 
      borrowed = false; 
     } 

     if (bigNumber.decimalDigits[i] < smallNumber.decimalDigits[i]) 
     { 
      bigNumber.decimalDigits[i] += 10; // Borrow from next number and add to this one 
      borrowed = true; 
     } 

     // Since we're working from the back of the list, always add to the front 
     newDecimalDigits.Insert(0, bigNumber.decimalDigits[i] - smallNumber.decimalDigits[i]); 
    } 

    for (int i = bigNumber.wholeDigits.Count - 1; i >= 0; i--) 
    { 
     if (borrowed) 
     { 
      bigNumber.wholeDigits[i] -= 1; 
      borrowed = false; 
     } 

     if (bigNumber.wholeDigits[i] < smallNumber.wholeDigits[i]) 
     { 
      bigNumber.wholeDigits[i] += 10; 
      borrowed = true; 
     } 

     newWholeDigits.Insert(0, bigNumber.wholeDigits[i] - smallNumber.wholeDigits[i]); 
    } 

    if (absThis < absOther) IsNegative = !IsNegative; 
    wholeDigits = newWholeDigits.GetRange(0, newWholeDigits.Count); 
    decimalDigits = newDecimalDigits.GetRange(0, newDecimalDigits.Count); 
    NormalizeLists(); 
} 

而且終於覆蓋了數字運營商:

public static LargeDecimal operator +(LargeDecimal first, LargeDecimal second) 
{ 
    if (first == null) return second; 
    if (second == null) return first; 

    var result = new LargeDecimal(first); 
    result.Add(second); 
    return result; 
} 

public static LargeDecimal operator -(LargeDecimal first, LargeDecimal second) 
{ 
    if (first == null) return second; 
    if (second == null) return first; 

    var result = new LargeDecimal(first); 
    result.Subtract(second); 
    return result; 
} 

public static bool operator >(LargeDecimal first, LargeDecimal second) 
{ 
    if (first == null) return false; 
    return first.CompareTo(second) > 0; 
} 

public static bool operator <(LargeDecimal first, LargeDecimal second) 
{ 
    if (second == null) return false; 
    return second.CompareTo(first) > 0; 
} 

public static bool operator >=(LargeDecimal first, LargeDecimal second) 
{ 
    if (first == null) return false; 
    return first.CompareTo(second) >= 0; 
} 
public static bool operator <=(LargeDecimal first, LargeDecimal second) 
{ 
    if (second == null) return false; 
    return second.CompareTo(first) >= 0; 
} 
public static bool operator ==(LargeDecimal first, LargeDecimal second) 
{ 
    return Equals(first, second); 
} 

public static bool operator !=(LargeDecimal first, LargeDecimal second) 
{ 
    return !Equals(first, second); 
} 

感謝有趣的挑戰!

+0

更新了我今晚寫的一些示例代碼,因爲我無法讓這個項目離開我的腦海! –

+0

你必須重寫Equals和GetHashCode。 – brz

+0

@brz我確實......可能必須滾動才能看到它 –

2

假設這個實施看起來是這樣的:

List<int> WholeList; 
List<int> FactionalList; 
bool IsNegative; 

,他們都生長在小數點離開,然後一個比較算法會是這樣的

  1. 先比較跡象。負面總是低於正面。
  2. WholeList比較長度越長,幅度越大(較大的數字取決於符號)
  3. 如果WholeList.Count相同。比較每個以最重要的開始的數字(又名WholeList [Count-1]第一個數字),首先在數字之間不同的數字將決定更大的幅度。
  4. 如果將它放入FractionalList中,然後用完一個列表中的數字。 FractionalList越長的數字越大。
相關問題