简体   繁体   English

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

[英]How can I check if an object representing a number is greater than another?

I'm currently in the process of writing a class that can represent an infinitely large number (in theory). 我目前正在编写一个可以表示无限大数量的类(理论上)。 The constructor of this class creates the object from a string value, which is why the number could be of an extremely large, yet unknown, size. 此类的构造函数从字符串值创建对象,这就是为什么数字可能很大但未知的原因。

The reason I started writing this class was because I wanted to be able to make a program that would be able to perform mathematical calculations with numbers of arbitrarily large size. 我之所以开始写这堂课,是因为我希望能够编写一个程序,该程序能够以任意大的数量执行数学计算。 Thus, I started writing a class that could handle values well over the standard ranges of integers, floats, doubles, (hopefully) decimals, etc. 因此,我开始编写一个可以很好地处理整数,浮点数,双精度数(希望是十进制)等标准范围内的值的类。

Here are the declarations and the main constructor for the class: 这是该类的声明和主要构造函数:

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

The class is basically a representation of a number through two lists of type int, where one list represents the digits that make up the whole partition of the number, and the other list represents the digits that make up the decimal partition of the number (if applicable). 该类基本上是通过两个类型为int的列表表示数字的方法,其中一个列表代表组成数字整个分区的数字,另一个列表代表组成数字的十进制分区的数字(如果适用)。

I have written an Add method which accepts two LargeDecimal objects, adds their values together, and returns a new LargeDecimal object with the sum as its value. 我已经编写了一个Add方法,该方法接受两个LargeDecimal对象,将它们的值相加,然后返回一个新的LargeDecimal对象,其总和为它的值。 Though incomplete, it does work with LargeDecimal objects that are whole numbers only, and are both positive or both negative (picture!). 尽管不完整,但它确实适用于LargeDecimal对象,这些对象仅是整数,并且都是正数或都是负数(图片!)。

I have realized that adding methods to compare two values (greater than / less than / equal to) would be extremely useful in calculations. 我已经意识到,添加用于比较两个值(大于/小于/等于)的方法在计算中非常有用。 However, I am not sure how to check whether the value of a LargeDecimal object is greater or less than the value of another LargeDecimal. 但是,我不确定如何检查LargeDecimal对象的值是大于还是小于另一个LargeDecimal的值。

There are cases where I can just compare the amount of items in the wholeDigits list, but that is only when the amounts of items are different for both values. 在某些情况下,我只能比较WholeDigits列表中的项目数量,但这仅是当两个值的项目数量都不同时。 I am unsure about how to compare two numbers such as: 15498765423654973246 and 15499111137583924246. 我不确定如何比较两个数字,例如:15498765423654973246和15499111137583924246。

And I think it will get more difficult if I will try and compare two fractional numbers: 8573819351.86931 and 8573809999.85999 而且我认为,如果我尝试比较两个小数部分,将会变得更加困难:8573819351.86931和8573809999.85999

I do not wish to use integer calculations in conjunction with place values (eg in the number 831, the value of the number 8 would be 8 * 100, the value of 3 would be 3 * 10, and the value of 1 would be 1 * 1), because I would like this class to be able to represent values of any given size and length and range (while an int cannot handle values up to 2147483647). 我不希望将整数计算与位置值一起使用(例如,在数字831中,数字8的值为8 * 100,数字3的值为3 * 10,数字1的值为1 * 1),因为我希望此类能够表示任何给定大小,长度和范围的值(而int不能处理最大2147483647的值)。

Any help regarding this would be highly appreciated! 任何对此的帮助将不胜感激! Thank you all! 谢谢你们!

Assuming that this implementation looks something like this: 假设此实现看起来像这样:

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

and they both grow away from the decimal point, then a comparison algorithm would go like this 并且它们都远离小数点增长,那么比较算法将像这样

  1. First compare signs. 首先比较迹象。 Negative is always less than positive. 负面总是小于正面。
  2. Compare lengths of WholeList, longer has larger magnitude (larger number is dependent on sign) 比较WholeList的长度,较长的则具有较大的大小(较大的数字取决于符号)
  3. If WholeList.Count the same. 如果WholeList.Count相同。 Compare each digit starting with most significant (aka WholeList[Count-1] first), first that are different between numbers will determine larger magnitude. 比较从最高有效位开始的每个数字(首先是WholeList [Count-1]),首先比较数字之间的差异将确定较大的幅度。
  4. If you make it into the FractionalList, and then run out of digits in one list. 如果将其放入FractionalList,然后在一个列表中用完数字。 The number with the longer FractionalList will be the larger magnitude. FractionalList较长的数字将具有更大的幅度。

I would start by implementing IComparable : 我将从实现IComparable开始:

public class LargeDecimal : IComparable<LargeDecimal>

And the implementation would look like: 实现看起来像:

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

Update 更新资料

This project was sitting on my brain at dinner tonight, so I went at it some more for fun. 这个项目是在今晚的晚餐上坐在我的大脑上的,所以我又去了一些有趣的事情。 Not sure if this is helpful, but figured I'd share what I came up with. 不确定这是否有帮助,但想知道我会分享我的想法。

First, I added fields to make the class actually work: 首先,我添加了一些字段来使类实际起作用:

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

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

Second, I overrode the ToString method so the numbers display nicely: 其次,我覆盖了ToString方法,以便数字显示良好:

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

Then I implemented the Equals methods so they work as expected for a number type: 然后,我实现了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;
    }
}

Next, I added some utility methods to help out with some upcoming tasks: 接下来,我添加了一些实用程序方法来帮助完成一些即将发生的任务:

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

Next, I added some constructors. 接下来,我添加了一些构造函数。 A default that sets the number to '0', one that parses a string, and another that copies the values from another LargeDecimal : 一个默认值,将数字设置为“ 0”,一个默认值解析一个字符串,另一个LargeDecimal值则从另一个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();
}

Then I implemented the Add and Subtract methods 然后我实现了加减法

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

And finally overrode the numeric operators: 最后覆盖数字运算符:

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

Thanks for the fun challenge!! 感谢您的挑战!!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM