17

アップデート

OK、いくつかの調査の後、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

decimal2 つの値を乗算し、結果を精度の低い引数の精度にトリムしたいとします。言い換えると:

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

decimal x = a / b;

Console.WriteLine(x);

上記の出力:

21.729560302171195125816619416

私が求めているのは、21.73代わりに返されるメソッドをどのように書くことができるかということです ( 123.4M4 つの有効数字があるため)。

明確にするために、両方の引数を呼び出しToString、各文字列の有効数字を数え、この数値を使用して計算結果を丸めることができることを理解しています。可能であれば、別の方法を探しています。

(また、有効数字を扱っているほとんどのシナリオでは、おそらく型を使用する必要がないこと認識しています。しかし、最初に述べたように、型には次の情報が含まれているように見えるdecimalので、私が尋ねているのです。私が知る限り、正確ではありません。)decimaldouble

4

3 に答える 3

3

はい、浮動小数点型とは異なり、System.Decimal はリテラルの桁数を追跡します。これは decimal.Parse() の機能であり、コード自体によって実行されるか、プログラム内のリテラルを解析するときにコンパイラによって実行されます。この情報を回復できます。このスレッドの私の回答のコードを確認してください。

値を計算した後に有効桁数を回復することは、私にはロングショットのように思えます。オペレーターがそれらを保存しているかどうかはわかりません。わかったことをお知らせください。

于 2010-09-10T14:32:11.083 に答える
3

を使用Decimal.GetBitsして生データを取得し、そこから計算できます。

残念ながら、現時点ではサンプル コードを記述する時間がありません。また、.NET 4 を使用している場合は、おそらくいくつかの操作に使用したいと思うでしょうBigIntegerが、これでうまくいくことを願っています。精度を調べてから元の結果を呼び出すMath.Roundだけでも、良いスタートになるかもしれません。

于 2010-09-10T10:08:33.127 に答える