如何检查表示数字的对象是否大于另一个?

时间:2015-01-02 20:40:08

标签: c# object math

我目前正在编写一个可以代表无限大数的类(理论上)。此类的构造函数从字符串值创建对象,这就是为什么该数字可能是一个非常大但未知的大小。

我开始写这门课的原因是因为我希望能够制作一个能够用任意大尺寸的数字进行数学计算的程序。因此,我开始编写一个类,它可以处理超过标准范围的整数,浮点数,双精度数,(希望)小数等的值。

以下是该类的声明和主要构造函数:

/// <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的值)。

对此有任何帮助将非常感谢!谢谢大家!

2 个答案:

答案 0 :(得分:2)

假设此实现看起来像这样:

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

并且它们都远离小数点,然后比较算法会像这样

  1. 首先比较一下迹象。负面总是低于正面。
  2. 比较WholeList的长度,较长的幅度较大(较大的数字取决于符号)
  3. 如果WholeList.Count相同。比较每个以最重要的数字开头的数字(又名WholeList [Count-1]优先),首先数字之间不同将确定更大的数量。
  4. 如果您将其设置为FractionalList,然后在一个列表中用完数字。具有较长FractionalList的数字将是更大的数量。

答案 1 :(得分: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;
    }
}

接下来,我添加了一些实用工具方法来帮助完成一些即将到来的任务:

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();
}

然后我实现了Add和Subtract方法

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);
}

感谢有趣的挑战!!