reactor / reactor-addons

Additional optional modules for the Reactor project

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

MathFlux sumBigDecimal looses scale of BigDecimal

kiwisincebirth opened this issue · comments

MathFlux sumBigDecimal looses scale of BigDecimal. And is not respecting standard BigDecimal behaviour.

Expected Behavior

The summation in the Flux should follow BigDecimal arithmetic, using a standard plus() operator provided by BigDecimal.

Actual Behavior

The summation logic is loosing precision, and the cause is related to the conversion of BigDecimal to it Double value

Steps to Reproduce

The following test case (Kotlin code) highlights the error. I have not tried this in Java, but assume the same issue would be prevalent in Java Code also.

    @Test
    fun test() {
        val ONE = BigDecimal("1.00")
        val ONE_PLUS_ONE = ONE.plus(ONE)
        val sumBigDecimal = MathFlux.sumBigDecimal(Flux.just(ONE,ONE)).block()

        assertThat(sumBigDecimal).isEqualTo(ONE_PLUS_ONE)
    }

the result if the above is

expected: 2.00
but was : 2.0
org.opentest4j.AssertionFailedError: 

Possible Solution

The issue seems to be in the Class MonoSumBigDecimal Line:77

BigDecimal bigDecimalValue = BigDecimal.valueOf(number.doubleValue());

where the Number being processed in the stream, is converted via Double back to a BigDecimal. For the general case of Number this is fine. but in this case the Number (IS) a bigdecimal and does not need to be cast in this way.

BigDecimal bigDecimalValue = (number instanceof BigDecimal) ?
    (BigDecimal) number : new BigDecimal(number.toString());

noting that number.toString() is my (OPINION) of how to convert a Number more generally into a BigDecimal, since the translation avoids the Double type.

I am fairly sure MonoAverageBigDecimal suffers from the same issue, since it has the same line of code indicated above.

Your Environment

Not Relevant the above test case fairly easy to reproduce

  • Reactor version(s) used: 3.4.3
  • JVM version (java -version): 11

That's a great catch @kiwisincebirth!

After having looked into your suggestion to use the BigDecimal(String value) constructor, I agree this seems like the best approach 👍

Would you like to submit a fixing PR for both sum and average?

See The pull Request #261 for the change I proposed.

I have updated all but one test to use verifyResult() instead of verifyBigDecimalResult() - It turns out the verifyBigDecimalResult() was actually masking the precision issue! Because verifyBigDecimalResult() uses compareTo() ==0 for its comparisons. Simply changing Tests to use verifyResult() - highlighted the stated issue.