8

指定された数値を日からミリ秒に変換するメソッドを作成しました。

private long expireTimeInMilliseconds;
...
public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = expireTimeInDays * 24 * 60 * 60 * 1000;
}

私は自分が何を間違えたかを理解するのに苦労しました。今私の質問: そのエラーはとても明白ですか?

修正された方法:

private long expireTimeInMilliseconds;
...
public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = ((long) expireTimeInDays) * 24 * 60 * 60 * 1000;
}

計算する前に整数をlongに変換しないと、完全に間違った結果が得られます。

4

10 に答える 10

8

それは明白ですか?Java をどれくらいの期間使用しているか、ミリ秒単位で処理しなければならなかった回数にもよると思います。もちろん、24日くらいまでは大丈夫なはずですが…

System.currentTimeMillis()最大のヒントは、 を返すことだと思いますlong。これは、ミリ秒数が大きくなる可能性があることを示しています。設定している変数の型も良いヒントになるはずです。

もちろん、整数で算術演算を行うと、結果がオーバーフロー時にラップアラウンドになることも知っておく必要があります。intそれが十分に自明であるかどうかは議論の余地がありますが、それはかなり無意味な議論です。C# では、オーバーフロー チェックを有効にしていれば、バグをすぐに発見できたはずです。

于 2009-02-04T17:14:06.730 に答える
7

はい、以前に行ったことがある場合は明らかです。数値の文字列が乗算されているのを見ると、整数オーバーフロー エラーについて自動的に考え始めるはずです。この場合、expireTimeInDaysが 24 を超えるとオーバーフローするように設定されています。技術的には、 integers を扱うときはいつでもオーバーフロー エラーについて考える必要がありますが、このようにそれらのグループを乗算することは非常に大きな危険信号であるはずです。

于 2009-02-04T17:14:53.827 に答える
3

これについては、Joshua Bloch と Neal Gafter による「Java Puzzlers」で取り上げられていることに興味があるかもしれません。

代替テキスト
(ソース: javapuzzlers.com )

この本には、他にも多くの Java の落とし穴、落とし穴、コーナー ケースが含まれています。

コメントを残したスターブルーに同意します。番号に L を追加します。

于 2009-02-04T17:30:55.030 に答える
3

オペランド変数とリテラル数値は int 型です。int データ型の最大値は 2^31 -1 です。したがって、このような大きな数では、int のデータ型がオーバーフローし、間違った答えに見えます。

最初の例では、int は、計算に発生する変数への代入時にのみ long に昇格されます。計算の結果は int です。

2 番目の例では、最初のオペランドを long にキャストし、計算を long に昇格させます。この場合、昇格により、計算結果は long になります。long データ型は、計算するのに十分な大きさです。

于 2009-02-04T17:17:50.093 に答える
2

いいえ、それは明らかではありません。

しかし、私を信じてください。さらに何年か練習を重ね、このようなバグを修正すると、整数オーバーフローについて非常に敏感になり、何も考えずに正しいことを実行できるようになります。

それは誰にでも起こったことです。間違いなく、悪いコード プラクティス、無知などの兆候はありません。

于 2009-02-04T17:13:17.310 に答える
1

これらのタイプのエラーを見つける静的分析ツール(findbugs)がいくつかあります。

コンピューターでの数値計算は難しい場合があります。操作の順序は、予期しない方法で精度と精度に影響を与える可能性があります。日付の計算も驚くほど難しい場合があります。多くの場合、自分で計算を行うよりも、Date / Calendarルーチンを使用する方が適切ですが、これらのルーチンは、Javaクラスライブラリで最適に設計されたルーチンではありません。

于 2009-02-05T03:05:25.307 に答える
1

これを書く別の方法は、

public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = (long) expireTimeInDays * 24 * 60 * 60 * 1000;
}

また

public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = expireTimeInDays * 24L * 60 * 60 * 1000;
}
于 2009-02-05T02:39:04.360 に答える
1

コードで FindBugs を使用すると、まさにこの問題が検出されます。「ICAST: long にキャストされた整数乗算の結果。」FindBugs の例はまさにあなたがやっていることです。ミリ秒単位で日数を計算します。

この問題は、初めて遭遇したときはわかりませんでした。

于 2009-02-05T02:47:55.327 に答える
1

他の回答に追加するだけで、過去にorpublic static final longなどの定数 ( ) を定義すると役立つことがわかりました。はるかに読みやすく、便利です。MILLISECS_DAYMILLISECS_HOUR

于 2009-02-04T18:33:43.107 に答える
0

私は自分の間違いを正当化しようとしているわけではありませんが、Java コンパイラが計算のかなり前に int をプロモートするのに十分スマートであるとしたら (計算が long 型の変数に割り当てられると)、それは素晴らしいことです。

ところで、C/C++ を使っていて、それが C プログラムだったら同じ問題を抱えていましたが、数年前までは、この種の操作にはもっと注意を払っていました。

次回はもっと注意を払います(またはpythonに切り替えます)... :D

于 2009-02-04T20:10:07.093 に答える