17

更新

好的,经过一番调查,在很大程度上要感谢 Jon 和 Hans 提供的有用答案,这就是我能够整理出来的。到目前为止,我认为它似乎运作良好。当然,我不会赌它的完全正确性。

public static int GetSignificantDigitCount(this decimal value)
{
    /* So, the decimal type is basically represented as a fraction of two
     * integers: a numerator that can be anything, and a denominator that is 
     * some power of 10.
     * 
     * For example, the following numbers are represented by
     * the corresponding fractions:
     * 
     * VALUE    NUMERATOR   DENOMINATOR
     * 1        1           1
     * 1.0      10          10
     * 1.012    1012        1000
     * 0.04     4           100
     * 12.01    1201        100
     * 
     * So basically, if the magnitude is greater than or equal to one,
     * the number of digits is the number of digits in the numerator.
     * If it's less than one, the number of digits is the number of digits
     * in the denominator.
     */

    int[] bits = decimal.GetBits(value);

    if (value >= 1M || value <= -1M)
    {
        int highPart = bits[2];
        int middlePart = bits[1];
        int lowPart = bits[0];

        decimal num = new decimal(lowPart, middlePart, highPart, false, 0);

        int exponent = (int)Math.Ceiling(Math.Log10((double)num));

        return exponent;
    }
    else
    {
        int scalePart = bits[3];

        // Accoring to MSDN, the exponent is represented by
        // bits 16-23 (the 2nd word):
        // http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx
        int exponent = (scalePart & 0x00FF0000) >> 16;

        return exponent + 1;
    }
}

我还没有彻底测试它。不过,这里有一些示例输入/输出:

价值精度
0 1 位数。
0.000 4 位数。
1.23 3 位数。
12.324 5 位数。
1.2300 5 位数。
-5 1 位数。
-5.01 3 位数。
-0.012 4 位数。
-0.100 4 位数。
0.0 2 位数。
10443.31 7 位数。
-130.340 6 位数。
-80.8000 6 位数。

使用此代码,我想我会通过执行以下操作来实现我的目标:

public static decimal DivideUsingLesserPrecision(decimal x, decimal y)
{
    int xDigitCount = x.GetSignificantDigitCount();
    int yDigitCount = y.GetSignificantDigitCount();

    int lesserPrecision = System.Math.Min(xDigitCount, yDigitCount);

    return System.Math.Round(x / y, lesserPrecision);
}

不过,我还没有真正完成这个工作。任何想分享想法的人:将不胜感激!


原始问题

假设我写了这段代码:

decimal a = 1.23M;
decimal b = 1.23000M;

Console.WriteLine(a);
Console.WriteLine(b);

以上将输出:

1.23
1.23000

我发现如果我使用decimal.Parse("1.23")foradecimal.Parse("1.23000")for这也有效b(这意味着这个问题适用于程序接收用户输入的情况)。

很明显,一个decimal值以某种方式“意识到”了我所说的精度decimal但是,除了ToString自身之外,我没有看到该类型的成员提供任何访问方式。

假设我想将两个decimal值相乘并将结果修剪到不太精确的参数的精度。换句话说:

decimal a = 123.4M;
decimal b = 5.6789M;

decimal x = a / b;

Console.WriteLine(x);

上述输出:

21.729560302171195125816619416

我要问的是:我怎么能写一个返回的方法21.73(因为123.4M有四个有效数字)?

明确一点:我意识到我可以调用ToString这两个参数,计算每个字符串中的有效数字,并使用这个数字对计算结果进行四舍五入。如果可能的话,我正在寻找一种不同的方式。

(我意识到,在您处理有效数字的大多数情况下,您可能不需要使用该decimal类型。但我问是因为,正如我在开头提到的那样,该decimal类型似乎包含有关精度,而double据我所知没有。)

4

3 回答 3

3

您可以使用它Decimal.GetBits来获取原始数据,并从中计算出来。

不幸的是,我目前没有时间编写示例代码 -BigInteger如果您使用 .NET 4,您可能希望使用一些操作来进行操作 - 但希望这会让您继续前进。仅仅计算出精度然后调用Math.Round原始结果可能是一个好的开始。

于 2010-09-10T10:08:33.127 回答
3

是的,与浮点类型不同,System.Decimal 跟踪文字中的位数。这是 decimal.Parse() 的一个特性,无论是由您的代码自己执行还是由编译器在解析程序中的文字时执行。您可以恢复此信息,查看我在此线程中的答案中的代码。

在对值进行数学运算后恢复有效位数让我觉得很遥远。不知道运营商是否保留它们,请告诉我们您的发现。

于 2010-09-10T14:32:11.083 回答
0

上面的解决方案很快就下床了,值为 1200,其中有效位数为 2,但函数返回 4。也许有一种一致的方式来处理这种情况,即检查以确保返回值不包含尾随零在没有小数部分的整数部分。

于 2017-02-02T17:17:47.813 回答