小数の「重要な数字」を取得する方法はありますか?
-
02-10-2019 - |
質問
アップデート
さて、いくつかの調査の後、ジョンとハンスから提供された有益な回答に感謝しますが、これは私がまとめることができたものです。これまでのところ、うまく機能しているようだと思います。もちろん、私はその完全な正しさに私の人生を賭けませんでした。
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;
}
}
私はそれをそれほど徹底的にテストしていません。ただし、いくつかのサンプル入力/出力は次のとおりです。
Value Precision 0 1 digit(s). 0.000 4 digit(s). 1.23 3 digit(s). 12.324 5 digit(s). 1.2300 5 digit(s). -5 1 digit(s). -5.01 3 digit(s). -0.012 4 digit(s). -0.100 4 digit(s). 0.0 2 digit(s). 10443.31 7 digit(s). -130.340 6 digit(s). -80.8000 6 digit(s).
このコードを使用して、このようなことをすることで目標を達成すると思います。
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")
にとって a
と decimal.Parse("1.23000")
にとって b
(つまり、この質問は、プログラムがユーザー入力を受信する場合に適用されます)。
明らかにa decimal
価値は、私がそれと呼ぶものを何とか「認識」しています 精度. 。ただし、メンバーはありません decimal
以外に、これにアクセスする方法を提供するタイプ ToString
自体。
2つを掛けたかったとします decimal
値を値とトリミングして、それほど正確ではない引数の精度になります。言い換えると:
decimal a = 123.4M;
decimal b = 5.6789M;
decimal x = a / b;
Console.WriteLine(x);
上記の出力:
21.729560302171195125816619416
私が尋ねているのは:返す方法を書くにはどうすればよいですか 21.73
代わりに(以来 123.4M
4つの重要な数字があります)?
明確にするために:私は電話できることに気づきました ToString
両方の引数で、各文字列の重要な数字を数え、この数値を使用して計算の結果を丸めます。私は探しています 違う 可能であれば、方法。
(私 また かなりの数字を扱っているほとんどのシナリオでは、おそらく使用する必要がないことを認識してください。 decimal
タイプ。しかし、私は最初に述べたように、 decimal
タイプ 表示されます 一方、精度に関する情報を含めること double
私が知る限りではありません。)
解決
使用できます Decimal.GetBits
生データを取得し、それから解決するために。
残念ながら、私は現時点でサンプルコードを書く時間がありません - あなたはおそらく使用したいと思うでしょう BigInteger
一部の操作では、.NET 4を使用している場合は、うまくいけば、これが進むことができます。正確さを解決してから電話をかけるだけです Math.Round
元の結果は良いスタートかもしれません。
他のヒント
はい、フローティングポイントタイプとは異なり、System.decimalはリテラルの数字数を追跡します。これは、あなた自身のコードによって実行されるか、プログラムで文字通りを解析するときに、コードによって実行されるかどうかにかかわらず、Decimal.Parse()の機能です。この情報を回復することができます。私の回答のコードをチェックしてください このスレッド.
価値について数学を行った後にかなりの数字の数を回復すると、ロングショットとして私を襲います。オペレーターがそれらを保存しているかどうかはわかりません。あなたが見つけたことを教えてください。
上記のソリューションは、重要な数字の数が2である1200のような値ですぐにベッドから落ちますが、関数は4が返されます。おそらく、そのような状況に対処するための一貫した方法、つまり、戻り値にトレーリングゼロが含まれないことを確認するためにチェックがあります部分的な部分のない整数部分。