java 如何比较 BigDecimals 以使我的测试通过?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39163731/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I compare BigDecimals to make my tests pass?
提问by AndreaNobili
I have the following same strange situation into a JUnit
test.
我有以下同样奇怪的情况进入JUnit
测试。
So I have this test method:
所以我有这个测试方法:
@Test
public void getNavInfoTest() throws ParseException {
TirAliquotaRamoI expectedObject = new TirAliquotaRamoI();
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Date date;
date = formatter.parse("2015-08-01");
Date dataInizio = formatter.parse("2015-08-01");
Date dataFine = formatter.parse("2100-12-31");
expectedObject.setDataElaborazione(date);
expectedObject.setTassoLordoAnnuoAppl(BigDecimal.ZERO);
expectedObject.setTassoGiornalieroNetto(BigDecimal.ZERO);
expectedObject.setAliquota(BigDecimal.ONE);
expectedObject.setDataInizio(dataInizio);
expectedObject.setDataFine(dataFine);
TirAliquotaRamoI tirAliquotaRamoI = pucManager.getNavInfo(date);
assertEquals(tirAliquotaRamoI.getAliquota(), expectedObject.getAliquota());
}
At the end I am simply testing if the tirAliquotaRamoI.getAliquota()
(obtained from a DB query) have the same value of the same field defined for the created expectedObject
:
最后,我只是测试tirAliquotaRamoI.getAliquota()
(从数据库查询中获得)是否具有为 created 定义的相同字段的相同值expectedObject
:
assertEquals(tirAliquotaRamoI.getAliquota(), expectedObject.getAliquota());
So the field for the expected object is created using the BigDecimal.ONE
constand and using the debugger I can see that its value is 1
.
所以预期对象的字段是使用BigDecimal.ONE
常量创建的,并使用调试器我可以看到它的值为1
.
And the tirAliquotaRamoI.getAliquota()
obtaind value is 1.000000000
而tirAliquotaRamoI.getAliquota()
obtaind值1.000000000
So, in theory, both represent the same value 1 but the test fail and I obtain:
所以,理论上,两者都代表相同的值 1 但测试失败,我得到:
java.lang.AssertionError: expected:<1.000000000> but was:<1>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:118)
at org.junit.Assert.assertEquals(Assert.java:144)
at com.fideuram.dbmanager.PucManagerTest.getNavInfoTest(PucManagerTest.java:90)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.accesspublic boolean equals(Object x) {
if (!(x instanceof BigDecimal))
return false;
BigDecimal xDec = (BigDecimal) x;
if (x == this)
return true;
if (scale != xDec.scale)
return false;
long s = this.intCompact;
long xs = xDec.intCompact;
if (s != INFLATED) {
if (xs == INFLATED)
xs = compactValFor(xDec.intVal);
return xs == s;
} else if (xs != INFLATED)
return xs == compactValFor(this.intVal);
return this.inflated().equals(xDec.inflated());
}
0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Why if both represent the ame 1.0 value? How can I fix this issue to pass the test?
为什么如果两者都代表 ame 1.0 值?如何解决此问题以通过测试?
回答by xenteros
The reason is how the BigDecimal
equals
is implemented. BigDecimal.ONE
is constructed as new BigDecimal(BigInteger.ONE, 1, 0, 1)
. The equals method is implemented in the following way (from JDK source code):
原因在于它是如何BigDecimal
equals
实现的。BigDecimal.ONE
被构造为 new BigDecimal(BigInteger.ONE, 1, 0, 1)
。equals 方法的实现方式如下(来自 JDK 源代码):
assertTrue(tirAliquotaRamoI.getAliquota().compareTo(expectedObject.getAliquota()) == 0);
So two BigDecimals
are equal only if they have the same scale. ONE
s scale is smaller than returned BigDecimal
's so they're not equal.
因此,BigDecimals
只有当它们具有相同的比例时,两个才相等。ONE
s 的比例小于返回的比例,BigDecimal
因此它们不相等。
I saw some answers saying that you can take floatValueof BigDecimal
. Well, you can't. BigDecimal
is used when dealing with bigger numbers so in this certain case it would work, but is a bad pattern.
我看到一些答案说你可以采取floatValueof BigDecimal
。嗯,你不能。BigDecimal
在处理更大的数字时使用,所以在这种情况下它会起作用,但这是一个糟糕的模式。
But fortunately we can use compareTo
method!
不过还好我们可以用compareTo
方法!
assertEquals(
tirAliquotaRamoI.getAliquota().stripTrailingZeros(),
expectedObject.getAliquota().stripTrailingZeros()
);
It's not perfect but will work!
它并不完美,但会起作用!
回答by Joachim Nilsson
BigDecimal comparison always uses the scale as well, therefore your tests are failing.
BigDecimal 比较也始终使用比例,因此您的测试失败。
See Javadocfor details.
有关详细信息,请参阅Javadoc。
If you are interested only in the value and not the scale, then consider using stripTrailingZeros() when comparing.
如果您只对值感兴趣而不对比例感兴趣,那么在比较时可以考虑使用 stripTrailingZeros()。
assertEquals(0, tirAliquotaRamoI.compareTo(expectedObject.getAliquota());
回答by Geraint Corneu
It looks to me like they have different scales.
在我看来,它们有不同的尺度。
Try compareTo:
尝试compareTo:
assertEquals((int)tirAliquotaRamoI.getAliquota(), (int)expectedObject.getAliquota());
Or if you care about matching the scales then you'll have to change the scale on either object using setScale(int newScale) to match the other.
或者,如果您关心匹配比例,那么您必须使用 setScale(int newScale) 更改任一对象的比例以匹配另一个。
Hope that helps.
希望有帮助。
Thanks to @David SN for the correction.
感谢@David SN 的更正。
回答by watery
Although I don't know JUnit methods, I guess the problem relates to:
虽然我不知道 JUnit 方法,但我想问题与:
public boolean equals(Object x)
Compares this BigDecimal with the specified Object for equality. Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).
公共布尔等于(对象 x)
将此 BigDecimal 与指定的 Object 进行比较是否相等。与 compareTo 不同,此方法仅在两个 BigDecimal 对象的值和比例相等时才认为它们相等(因此,通过此方法进行比较时,2.0 不等于 2.00)。
(see BigDecimal.equals() docs)
Probably, assertEquals()
is using the aforementioned method, which compares both the valueand the scaleof your numbers.
可能assertEquals()
是使用上述方法,该方法比较数字的值和比例。
You may want to try an approach with assertTrue()as suggested in x870eaddd's answer
您可能想按照x870eaddd 的回答中的建议尝试使用assertTrue()的方法
回答by Lexo
Try this :
试试这个 :
##代码##I think AssertEquals
can only compare two variable from same type (int
to int
, double
to double
...). In your test, one is a decimal and the other is not, so it probably can't compare them.
我认为AssertEquals
只能比较相同类型的两个变量(int
to int
,double
to double
... )。在你的测试中,一个是小数,另一个不是,所以它可能无法比较它们。
回答by Nghia Do
What is data type for tirAliquotaRamoI.getAliquota()
? Is it BigDecimal
too because BigDecimal.ONE
is a BigDecimal
data type.
什么是数据类型tirAliquotaRamoI.getAliquota()
?是不是BigDecimal
也因为BigDecimal.ONE
是一个BigDecimal
数据类型。
If tirAliquotaRamoI.getAliquota() is Long or Int or Float, you can use
如果 tirAliquotaRamoI.getAliquota() 是 Long 或 Int 或 Float,则可以使用
assertEquals(tirAliquotaRamoI.getAliquota(), BigDecimal.ONE.intValue())
assertEquals(tirAliquotaRamoI.getAliquota(), BigDecimal.ONE.intValue())
or
或者
assertEquals(tirAliquotaRamoI.getAliquota(), BigDecimal.ONE.longValue())
assertEquals(tirAliquotaRamoI.getAliquota(), BigDecimal.ONE.longValue())