編集:この質問は2つのトピックをカバーしています:
floatの代わりにJavadoubleを常に使用する必要がない理由はありますか?
floatを使用する場合のこのテストコードは失敗し、doubleの代わりにfloatを使用することが唯一の違いであるため、理由が明確でないため、この質問をします。
public class BigDecimalTest {
@Test public void testDeltaUsingDouble() { //test passes
BigDecimal left = new BigDecimal("0.99").setScale(2,BigDecimal.ROUND_DOWN);
BigDecimal right = new BigDecimal("0.979").setScale(2,BigDecimal.ROUND_DOWN);
Assert.assertEquals(left.doubleValue(), right.doubleValue(), 0.09);
Assert.assertEquals(left.doubleValue(), right.doubleValue(), 0.03);
Assert.assertNotEquals(left.doubleValue(), right.doubleValue(), 0.02);
Assert.assertNotEquals(left.doubleValue(), right.doubleValue(), 0.01);
Assert.assertNotEquals(left.doubleValue(), right.doubleValue(), 0.0);
}
@Test public void testDeltaUsingFloat() { //test fails on 'failing assert'
BigDecimal left = new BigDecimal("0.99").setScale(2,BigDecimal.ROUND_DOWN);
BigDecimal right = new BigDecimal("0.979").setScale(2,BigDecimal.ROUND_DOWN);
Assert.assertEquals(left.floatValue(), right.floatValue(), 0.09);
Assert.assertEquals(left.floatValue(), right.floatValue(), 0.03);
/* failing assert */ Assert.assertNotEquals(left.floatValue() + " - " + right.floatValue() + " = " + (left.floatValue() - right.floatValue()),left.floatValue(), right.floatValue(), 0.02);
Assert.assertNotEquals(left.floatValue(), right.floatValue(), 0.01);
Assert.assertNotEquals(left.floatValue(), right.floatValue(), 0.0);
}}
失敗メッセージ:
java.lang.AssertionError: 0.99 - 0.97 = 0.01999998. Actual: 0.9900000095367432
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failEquals(Assert.java:185)
at org.junit.Assert.assertNotEquals(Assert.java:230)
at com.icode.common.BigDecimalTest.testDeltaUsingFloat(BigDecimalTest.java:34)
このテストが失敗する理由と、floatの代わりに常にdoubleを使用するべきではない理由はありますか?もちろん、ダブル以外の理由はフロートよりも広いです。
編集:面白いことに、Assert.assertNotEquals(double、double、delta)はどちらの場合もdoubleを取るので、失敗したテストで返されるフロートはとにかくdoubleとして拡大されますが、なぜテストが失敗するのですか?
編集:この他の質問が関連している可能性がありますが、確かではありません:16進数は同じではありません
編集:この質問の16進数の答えは同じではありませんが、 floatの.99の科学的表現IEEE 754は、同じ値のdoubleとは異なると結論付けることができます。これは丸めによるものです。
したがって、これを取得します。
上記の単体テストの最大デルタは0.02であり、(失敗したテストの)0.01999998はデルタ値を下回っているため、数値は同じであるように見えますが、テストは失敗していないと主張しています。
みんなあなたはこれすべてに同意しますか?
BigDecimalのドキュメントには、floatValue()
ラウンドの方法については記載されていません。私はそれが最も近い、均等に結ぶことを使用していると思います。
left
およびright
は、それぞれ.99および.97に設定されます。これらをdouble
最も近い丸めモードに変換すると、結果は0.9999999999999999911182158029987476766109466552734375(16進浮動小数点、0x1.fae147ae147aep-1)および0.9699999999999999733546474089962430298328399658203125(0x1.f0a3d70a3d70ap-1)になります。それらを引くと、結果は0.020000000000000017763568394002504646778106689453125になり、明らかに.02を超えます。
.99と.97をに変換するfloat
と、結果は0.9900000095367431640625(0x1.fae148p-1)と0.9700000286102294921875(0x1.f0a3d8p-1)になります。これらを差し引くと、結果は0.019999980926513671875になり、明らかに.02未満になります。
簡単に言えば、10進数を浮動小数点に変換すると、丸めが上下する場合があります。これは、最も近い表現可能な浮動小数点値に対して、数値がたまたまどこにあるかによって異なります。それが制御または分析されていない場合、それは実質的にランダムです。したがって、予想よりも大きな値になる場合もあれば、小さい値になる場合もあります。
のdouble
代わりに使用しても、上記のような結果が発生しないfloat
ことは保証されません。double
この場合の値が正確な数学的値を超え、値が超えなかったのは偶然ですfloat
。他の数字では、逆になる可能性があります。例えば、とはdouble
、.09-.07
以下0.02以上であるが、とfloat
.09f、 - .07f`が0.02よりも大きいです。
浮動小数点演算のハンドブックなど、浮動小数点演算の処理方法に関する情報はたくさんあります。StackOverflowの質問でカバーするには大きすぎるテーマです。その上に大学のコースがあります。
多くの場合、今日の一般的なプロセッサでdouble
は、float
;よりも使用するための追加費用はほとんどありません。単純なスカラ浮動小数点演算をするためのほぼ同じ速度で実行されるdouble
とfloat
。データが多すぎて(ディスクからメモリまたはメモリからプロセッサに)転送する時間が重要になる場合、データがディスク上で占めるスペースが大きくなる場合、またはソフトウェアがプロセッサのSIMD機能を使用する場合、パフォーマンスの違いが発生します。(SIMDを使用すると、プロセッサは複数のデータに対して同じ操作を並行して実行できます。現在のプロセッサは通常、float
SIMD操作の帯域幅をSIMD操作の約2倍にするdouble
か、double
SIMD操作をまったく提供しません。)
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加