hnwの日記

Mono環境のC#はdecimal型を正しく丸められない

Mono環境のC#において、decimal型リテラルもしくはdecimal型の演算中にdecimal型の精度を超えた場合に、最近接の数に丸められないバグと偶数丸めのはずが四捨五入になるバグを見つけました。

using System;

class DecimalTest
{
    static void Main()
    {
        decimal dcm1 = 987654321098765432109876543.25m;
        decimal dcm2 = 987654321098765432109876543.25999m;
        
        System.Console.WriteLine("dcm1 = {0}", dcm1); // 987654321098765432109876543.2
        System.Console.WriteLine("dcm2 = {0}", dcm2); // 987654321098765432109876543.2(バグ?)
        
        decimal dcm3 = 987654321098765432109876543.2m;
        decimal dcm4 = 0.05m;
        decimal dcm5 = 0.05999m;
        
        System.Console.WriteLine("dcm3+dcm4 = {0}", dcm3+dcm4); // 987654321098765432109876543.3(バグ?)
        
        System.Console.WriteLine("dcm3+dcm5 = {0}", dcm3+dcm5); // 987654321098765432109876543.3
    }
}


上の例で言うとdecimalは10進28桁までしか表現できないので、小数点第2位以下の「5」や「5999」が丸められます。


dcm1はプログラム中のdecimal型のリテラルを解釈する例です。この例について言うと、表現できるうちで最近接の2数の「ど真ん中」になるので、偶数丸めが起きて「…543.2」と切り捨てられています。これは期待通りの挙動です。


一方、dcm2の方はど真ん中より大きい数ですので、「…543.3」になるべきです。しかし、そうはならずに切り捨てられています。これはバグでしょう。


次に、演算途中に似たような状況になる例としてdcm3とdcm4の和を取ってみました。こちらはdcm1と全く同じ数を表すはずなので偶数丸めで切り捨てが起こるはずですが、なぜか切り上げられています。これもバグでしょう。


最後に念のためdcm3とdcm5の和を取ってみました。これはdcm2と同じ数になりますが、dcm2のときと異なり、期待通り切り上げが起こっています。


ちなみに、.NET環境では上記すべてが期待通りに動きます。


今回指摘した内容はどちらもバグだろうと思いますが、ここから先どこをどう追えばいいのか(もしくはどこに投げればいいのか)イマイチわからないので、どなたか教えて頂けると幸いです。