異なる結果を持つdoubleからintへの明示的なキャスト

基本的にこれを行うAPIを使用しています

var t = TimeSpan.MaxValue;
int x = (int)t.TotalMilliseconds;

ここでxはSystem.Threading.WaitHandle.WaitOne(int)に渡されます。

問題は、このコードが開発環境とステージング環境で実行されても、エラーは発生しませんが、運用環境で実行される場合はスローされます。

Exception: System.ArgumentOutOfRangeException
Message: Number must be either non-negative and less than or equal to Int32.MaxValue or -1.
Parameter name: millisecondsTimeout

単純なコンソールアプリケーション(x86とx64の両方)でこれをテストすると、x = -2147483648(int.MinValue)の結果が得られますが、直接ウィンドウでコードを実行するとx = 1566804069になります。

何が起こっている?

注:ステージングとプロダクションはすべて単一のVMから複製されるため、それらの間に違いはありません

THIS IS CODE THAT WE CANNOT CHANGE! Otherwise I wouldn't be asking this question.

3
@トニー私は十分な情報がないので、私は投稿した。それは非常に奇妙です。私はバグレポートを提出しました。私たちはこのAPIを使用する唯一の人ではありませんので、誰も報告していない場合は奇妙なことがありますが、明らかに間違ったコードです。
追加された 著者 Dustin Davis,
@Ramhoundは、太字で質問しています。「このコードは変更できません!そうでなければ、私はこの質問をしません」。
追加された 著者 phoog,
@Ramhound私が最初にこのページを少なくとも15分前に開いたときにそこにいた。
追加された 著者 phoog,
@Ramhound OKしかし、私はあなたのコメントがちょうど数分前に追加されていたようだと思われるあなたのコメントを読む。コメントを削除して再投稿していますか?時間に依存することについて話しているとき、それを行うことは非常に混乱しています。たとえば、6分前に返信したコメントは、2分前に投稿されたようです。
追加された 著者 phoog,
@Tonyいいえ、チェックされていればオーバーフロー例外になります。引用された例外は、チェックされていないのために継続期間が折り返しているか、間隔が負であったかを 示唆しています。
追加された 著者 Marc Gravell,
もし checkunchecked が問題であった場合、それは実際にはエラーですが、コード>)
追加された 著者 Marc Gravell,
@ダスティンは動作しません。最大時間は10,675,199日である。ミリ秒単位のint.MaxValueは25日間です。それはフィットするつもりはありません! -
追加された 著者 Marc Gravell,
VMはどちらのシステムでも同じですが、基本となるハードウェアはどうですか?彼らは別のCPUを使用していますか?
追加された 著者 Gabe,
コンバージョンがオーバーフローし、2つの異なる結果が得られ、キャストをlongまたはInt64にキャストする
追加された 著者 Emmanuel N,
コンソールアプリケーションを作成したときに動作すると言いましたか?しかし、あなたはまたそれが負の値を渡していると言った。テストコンソールのアプリケーションが負の値でWaitOne()を呼び出し、例外をスローしていないことをsayignしていますか?
追加された 著者 Dylan Smith,
コメントを削除しました。
追加された 著者 Security Hound,
@phoog - 彼は最近追加したばかりです。
追加された 著者 Security Hound,
私のコメントを削除し、可能な修正を加えた回答を追加しました。
追加された 著者 Tony Lee,
@DustinDavis - 私はGabeが正しい質問をしたと思う - 生産システムはIntel SSE2をサポートしているのか?私のマシンでは、変換にはcvttsd2si(SSE2が必要)が使用され、Int.MinValueが生成されます。
追加された 著者 Tony Lee,
@マルク - 私は遅いです。私は、この質問には十分な情報がないことを意味すると思います。
追加された 著者 Tony Lee,

4 答え

TimeSpan.MaxValue.TotalMillisecondsは922337203685477に等しい倍数で、Int32.MaxValue(2147483647)よりも大きい値です。この場合のキャストは、実装固有の(技術的には未定義です。@ phoogのコメントを参照してください)、おそらくCPUに依存します。見て。

あるケースでは、キャストは System.Threading.WaitHandle.WaitOne(int)に受け入れられる値になり、それ以外の場合は受け入れられません。

これは、使用しているライブラリのバグのようです。 TimeSpanを引数とするWaitOneオーバーロードがあるため、なぜそれらを使用しなかったのか分かりません。ライブラリを変更できない場合は、運が悪いです。

4
追加された
うん、それはバグです。非常に刺激的です。
追加された 著者 Dustin Davis,
@MarcGravellも、ECMA 334では「実装固有」ではありませんが、未定義です。チェックされていないコンテキストでは、変換は常に成功し、次のように進みます。 •値は、ゼロに向かって最も近い整数値に丸められます。この整数値が宛先タイプの範囲内にある場合、この値は変換の結果です。 •そうでない場合、変換の結果は宛先タイプの不特定の値です。
追加された 著者 phoog,
@Marc Gravell ECMA 335、partition III、3.27 conv。 - data conversion "浮動小数点型を整数に変換する際にオーバーフローが発生した場合、または浮動小数点値が整数に変換される場合はNaN返される値は不特定です。
追加された 著者 phoog,
@phoog aaahhh、 TotalMillisecondsdouble です!私の悪い;次にはい。私はそれを long として考えていました。この場合、単に余分なビットをトリムします。しかし、それは意味をなさない double のためです
追加された 著者 Marc Gravell,
実装固有であると確信していますか?他の操作(追加、乗算など)の振る舞いは、オーバーフロー条件(チェックされているかチェックされていない状態)で完全に指定されています - 狭い変換が適格でない
追加された 著者 Marc Gravell,

これが同一のVMで起こる唯一の方法は、CPUがステージングマシン上のCPUとプロダクションマシンで異なる場合です。たとえば、 Gabe は、 zdan のコメントに尋ねました。 com/questions/7920595 /明示的なキャスティング - 二重からintの異なる結果/ 7921005#7921005 ">彼の答え。

So specifically as to what is going on. For machines that support SSE2, the cvttsd2si instruction is used by .NET to convert the double into an int, where overflow is mapped to 0x80000000 (Int.MinValue). On machines w/o SSE2 support, I could only look at the Rotor sources, and in jithelpers.cpp, it simply casts the double to an int32 - which w/o SSE2 on VC10 C++, ends up returning the value in the lower 32 bits so the value passed to wait should be 1566804069 (0x5D638865) as you saw in the immediate window.

CPUが異なっていて、SSE2をサポートしていないマシンにコードを変更しないで "修正"しなければなりません。実稼働サーバーのCPUとステージングサーバーを確認するには、 SSE2 wikipediaの項目を参照してください。運が良ければ、サーバーのBIOS(またはVMの設定/ BIOS)で無効にすることができます。

あなたが大胆であれば、ILにパッチを当てて問題を修正することができます。コードが本当に望んでいたのはタイムアウトとして-1です。これは "永遠に待ってください"です。 ilasmとildasmを使用することで、ソースなしで修正できるかもしれません(私はこれを変更できないと仮定しています)。私は、このプログラムをILに組み込み、ILを編集し、最後に ilasm test.il/exe というテストプログラムを作成しました。/code>を使用して新しいアセンブリを作成します。以下は私のイリノイがどのように見えたか、どうやってそれを修正したかです。

// bad code
// var t = TimeSpan.MaxValue;
IL_0008:  call       instance float64System.TimeSpan::get_TotalMilliseconds()

// int x = (int)t.TotalMilliseconds;
IL_000D:  conv.i4   //This is the line that becomes cvttsd2si when jitted
IL_000E:  stloc.2

// wh.WaitOne(x);
IL_000F:  ldloc.0
IL_0010:  ldloc.2
IL_0011:  callvirt   instance bool System.Threading.WaitHandle::WaitOne(int32)

この問題を解決するには、Wait oneを呼び出す前にx(ここでは位置2)を-1で再ロードします。

// fixed code
// var t = TimeSpan.MaxValue;
IL_0008:  call       instance float64System.TimeSpan::get_TotalMilliseconds()

// int x = (int)t.TotalMilliseconds;
IL_000D:  conv.i4   //This is the line that becomes cvttsd2si when jitted
IL_000E:  stloc.2

// x = -1;//Fix by forcing x to -1 (infinite timeout)
          ldc.i4.m1 //push a -1
          stloc.2   //pop and store it in 'x'

// wh.WaitOne(x);
IL_000F:  ldloc.0
IL_0010:  ldloc.2
IL_0011:  callvirt   instance bool System.Threading.WaitHandle::WaitOne(int32)

この場合、 'x'はローカル#2であることに注意してください - メソッドの一番上にあるILは正しい#を与えるので、stloc.2の2は#xが割り当てられているものに変更する必要があります。私の例ではラベルIL_0010のWaitOneを呼び出す直前のldloc命令の#にマッチします。

4
追加された
私はあなたに努力の答えを与えます。私は1)バグを報告して、それを修正して、新しいバージョンが出てきたことを知らせてくれました2)PostSharpを使ってMillisecondsプロパティをインターセプトし、それを適切な値に変更する方法あなたが何を提案するか)。しかし、あなたは素晴らしい解決策を提供しました。
追加された 著者 Dustin Davis,

コンバージョンがオーバーフローして、異なるシステムで異なる結果が得られるのはなぜですか。 代わりにlongを使用する

  var t = TimeSpan.MaxValue;
  long x = (long)t.TotalMilliseconds;
0
追加された
私たちが消費しているので、これを変更することはできません。そうでなければ、私はこの質問をしていません。
追加された 著者 Dustin Davis,
いいえ、 WaitOne(-1)を呼び出すだけで正しいことはできますが、OPはコードを操作するのではなく環境を制御しているため。
追加された 著者 Gabe,

TimeSpan.MaxValueはWaitOne()に渡すには大きすぎるInt64.MaxValueに相当します。大きな値を渡す場合は、Int32.MaxValueを使用してください。

0
追加された
私はILSpyを使ってコードを見ることができます。 wiki.sharpdevelop.net/ILSpy.ashx コードを修正する予定はありません。なぜ2つのシステムで動作するのか分かりませんが、別のシステムでは動作しません。
追加された 著者 Dustin Davis,
これはコードでは変更できません。あなたは答えが無効です。
追加された 著者 Dustin Davis,
私はそれを取得していない、あなたはクラッシュしているコードがありますが、それを変更することはできません?どのようにこれを修正すると思いますか?
追加された 著者 Dylan Smith,