2017-12-26 39 views
0

私は10進数で時間外労働を計算する、C#で小さなソフトウェアを作成しました。私はそれが常に1時間の最も近い10分の1になるように設計しました。私が抱えている問題は、ちょうど2時間12分または3時間12分の時間を計算しようとすると、私は間違った結果を得ることです。関連するコードは次のとおりです。TimeSpan計算エラー

DateTime start = new DateTime(
         dtpDateStart.Value.Year, 
         dtpDateStart.Value.Month, 
         dtpDateStart.Value.Day, 
         dtpTimeStart.Value.Hour, 
         dtpTimeStart.Value.Minute, 
         0); 

     DateTime end = new DateTime(
      dtpDateEnd.Value.Year, 
      dtpDateEnd.Value.Month, 
      dtpDateEnd.Value.Day, 
      dtpTimeEnd.Value.Hour, 
      dtpTimeEnd.Value.Minute, 
      0); 

     TimeSpan subtotal = end - start; 

     double subtotalRounded = subtotal.TotalHours; 
     subtotalRounded = (Math.Floor(subtotalRounded * 10)/10); 

dtpDateStartdtpTimeStartdtpDateEnddtpTimeEndが勝つフォームプロジェクト内のすべての日付時刻ピッカーコントロールです。

このコードをデバッグすると、返される値はsubtotalRoundedで、2.2および3.2の代わりに2.1999999999999997または3.1999999999999997になります。これは、4時間12分以上または1時間12分以下の値では発生しないようです。

とにかく、これらの2つの値だけで丸め誤差が発生するのはなぜですか?誰か提案やコメントがありますか?メソッドを間違って使用していますか、これを行うにはより良い方法がありますか?

+3

これは浮動小数点の「エラー」(意図的な引用符)のようです。丸めの問題(特に小数点以下の桁数)を望まない場合:double - soを使用しないでください:分単位(または秒単位、または任意の精度)で整数として動作します。小数は次のようになります:分(秒、何でも)を小数に変換 –

答えて

0

これは単純に浮動小数点値の動作方法ですが、通常は計算時ではなく表示時に扱います。つまり、string.Format( "{0:f2}"、value)を実行すると、要求された桁数に丸められます。

+3

部分的に間違っています。書式は丸められません。残りの桁を単に削除します。丸めは、カットオフよりも大きな値になる可能性があるので、非常に重要なのです。 – Christopher

2

doubleを使用します。ビット表現は正確で、10進表現は正確ではありません。代わりにdecimalデータ型を使用してください。 Microsoft状態この:

WHERE句の検索条件、 特に=と<>オペレーターにフロートまたは実数の列を使用することは避けてください。 floatと実際の の列を>または<の比較に制限するのが最善です。

変更これらの2つのコード行これに

double subtotalRounded = subtotal.TotalHours; 
subtotalRounded = (Math.Floor(subtotalRounded * 10)/10); 

decimal subtotalRounded = subtotal.TotalHours; // See data type! 
subtotalRounded = (Math.Floor(subtotalRounded * 10)/10); 

またはさらに短いこの:

decimal subtotalRounded = = (Math.Floor(subtotal.TotalHours * 10)/10); 

あなたは適切なデータ型変換を行う必要があります。その場合は、Convertクラスのヘルパーメソッドを使用できます。

+1

これは、古くからの浮動小数点ミスコンセプトのようなものです。この動画ではうまく説明されています:https://www.youtube.com/watch?v=PZRI1IfStY0基本的に精度が必要な場合は、これまでずっと浮きを使うことはできません。 – Christopher

0

特定の番号をdoubleに正確に表すことはできません。 decimalを使用するか、分単位で(好ましくは)作業し、出力にdecimalを使用して60で割ります。

0

あなたは浮動小数点数については大丈夫でした。私はすべてを10進数に変換し、今は動作します。これを行うためのよりエレガントな方法があると確信していますが、私のコードはここにあります。

 DateTime start = new DateTime(
         dtpDateStart.Value.Year, 
         dtpDateStart.Value.Month, 
         dtpDateStart.Value.Day, 
         dtpTimeStart.Value.Hour, 
         dtpTimeStart.Value.Minute, 
         0); 

     DateTime end = new DateTime(
      dtpDateEnd.Value.Year, 
      dtpDateEnd.Value.Month, 
      dtpDateEnd.Value.Day, 
      dtpTimeEnd.Value.Hour, 
      dtpTimeEnd.Value.Minute, 
      0); 

     TimeSpan subtotal = end - start; 

     decimal decSubtotalMinutes = Convert.ToDecimal(subtotal.TotalMinutes); 
     decSubtotalMinutes = decSubtotalMinutes/60; 
     decSubtotalMinutes = (Math.Floor(decSubtotalMinutes * 10)/10); 

小数点を使うと正しく計算されます。ダブルを使用すると浮動小数点エラーが発生することはわかりませんでした。新しいプログラマーを助けてくださったみなさん、ありがとう!