50

Decimalデータ型にEpsilonフィールドがないのはなぜですか?

マニュアルによると、値の範囲decimalは±1.0×10e−28〜±7.9×10e28です。

の説明Double.Epsilon

Doubleゼロより大きい最小の正の値を表します

したがって、そのようDecimalな(自明ではない)値もあるようです。しかし、なぜ簡単にアクセスできないのでしょうか。

+ 1.0×10e−28は、ゼロより大きい正の10進数の最小値であることを理解しています。

decimal Decimal_Epsilon = new decimal(1, 0, 0, false, 28); //1e-28m;

ちなみに、Decimalデータ型の内部表現に関する情報を提供するいくつかの質問があります。

Epsilonこれが役立つ例です。

いくつかのサンプリングセットからの値の加重和と、取得したサンプルの加重(またはカウント)の合計があるとします。次に、加重平均値を計算します。しかし、重み(またはカウント)の合計がまだゼロである可能性があることを私は知っています。ゼロによる除算を防ぐためif... else...に、ゼロをチェックすることができます。または私はこのように書くことができます:

T weighted_mean = weighted_sum / (weighted_count + T.Epsilon)

このコードは私の目には短いです。または、代わりに、をスキップして+ T.Epsilon、代わりに次のように初期化することもできます。

T weighted_count = T.Epsilon;

実際の重みの値がに近づくことは決してないことがわかっているときに、これを行うことができますEpsilon

また、一部のデータ型とユースケースでは、ブランチが含まれないため、これはさらに高速になる可能性があります。私が理解しているように、ブランチが短い場合でも、プロセッサは計算のために両方のブランチを取ることができません。そして、ゼロが50%の割合でランダムに発生することを知っているかもしれません:=)Decimalの場合、速度の側面は重要ではないか、最初のケースでは積極的に役立つ可能性があります。

私のコードは一般的なもの(たとえば、生成されたもの)であり、小数用に個別のコードを記述したくありません。Decimalしたがって、他の実数値型と同様のインターフェイスを備えていることを確認したいと思います。

4

3 に答える 3

14

その定義に反して、イプシロンは実際には、値の2進表現と10進表現の間の変換のあいまいさを排除するために使用される概念です。たとえば、10進数の0.1には単純なバイナリ表現がないため、doubleを0.1として宣言すると、実際にはその値がバイナリの近似表現に設定されます。その2進表現の数値を(数学的に)それ自体に10回追加すると、約1.0の数値が得られますが、正確ではありません。イプシロンを使用すると、数学を曖昧にすることができ、それ自体に追加された0.1の近似表現は、0.2の近似表現と同等であると見なすことができます。

表現の性質によって引き起こされるこの近似は、すでに10進表現である10進値型には必要ありません。これが、実際の数値とそれ自体が近似値ではない数値(つまり、質量ではなくお金)を処理する必要がある場合は常に、使用する正しい浮動小数点タイプが10進数であり、2倍ではない理由です。

于 2012-08-02T16:46:08.210 に答える
2

96 ビットの仮数部だけを考えると、Decimal 型は、96 ビットのセットで構築された BigInteger の逆数に等しいイプシロンを持つと考えることができます。これは明らかに、現在の固有の値の型で表すには小さすぎる数値です。

つまり、このような小さな部分を表すには、「BigReal」値が必要になります。

率直に言って、それはイプシロンの「粒度」にすぎません。次に、GIVEN 10 進値の「実際の」イプシロンに到達するには、指数 (GetBits() からの最も高い Int32 のビット 16 ~ 23) を知る必要があります。

明らかに、Decimal の「イプシロン」の意味は可変です。指数で粒度イプシロンを使用して、GIVEN 10 進数の特定のイプシロンを考え出すことができます。

しかし、次のかなり問題のある状況を考えてみてください。

[TestMethod]
public void RealEpsilonTest()
{
    var dec1 = Decimal.Parse("1.0");
    var dec2 = Decimal.Parse("1.00");
    Console.WriteLine(BitPrinter.Print(dec1, " "));
    Console.WriteLine(BitPrinter.Print(dec2, " "));
}

DEC1: 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000 0100

DEC2; 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000000 000000000 000000000

解析された 2 つの値は一見等しいように見えますが、それらの表現は同じではありません!

この話の教訓は... Decimal を理解していると考える前に、Decimal を完全に理解するように十分に注意してください!!!

ヒント:

(理論上) Decimal のイプシロンが必要な場合は、([StructLayout[LayoutKind.Explicit])Decimal(128 ビット) と BigInteger(96 ビット) と Exponent(8 ビット) を組み合わせた UNION を作成します。Epsilon のゲッターは、粒度イプシロンと指数に基づいて正しい BigReal 値を返します。もちろん、BigReal定義が存在することを前提としています (かなり前から聞いていましたが、来る予定です)。

ちなみに、粒度イプシロンは定数または静的フィールドになります...

static grain = new BigReal(1 / new BitInteger(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF });

宿題: BigInteger への最後のバイトは0xFFor 0x7F(またはまったく別のもの) にする必要がありますか?

追伸: これらすべてが予想よりもかなり複雑に聞こえる場合は、... コンプ サイエンスはかなりの見返りがあると考えてください。/-)

于 2014-09-18T03:06:12.140 に答える