Search code examples
javakotlinfloating-pointdoublebigdecimal

Multiply decimals in Kotlin


I'm trying to multiply two crypto currencies together, their numbers are for example, 0.00200 and 0.00300. I've defined them as floats, I've also tried doubles and big decimals. But I'm struggling to get the output I'm expecting.

Here's the test:

class MultiplyDecimalTest {

    @Test
    fun `test can multiply two decimal integers`() {
        val a = 0.00200
        val b = 0.00400
        val expected = 0.00800
        val actual = multiplyDecimal(a, b).toDouble()
        assertEquals(expected, actual)
    }
}

Current function:

fun multiplyDecimal(a: Double, b: Double): BigDecimal {
    return BigDecimal(b).multiply(BigDecimal(a))
}

I'm new to Kotlin/Java, so I suspect I'm using the wrong type of integer perhaps.

The actual outcome is: 8.0E-6 - I understand the E-6 is an exponent, I'd like to be able to format it like the original values as well.


Solution

  • A key part of BigDecimal is that you must use it literally the whole way through: passing Double into BigDecimal destroys any benefit you would get from BigDecimal.

    You must use constructor taking a String, BigDecimal("0.00200") and BigDecimal("0.00400"), with the quotes. Then call multiply on them. You must not call doubleValue() on the result.

    Equality testing on BigDecimal also adds complications, requiring exactly the same scale. Consider using compareTo like this, assertTrue(expected.compareTo(actual) == 0).

    Another issue: Your math is incorrect. 0.00200 * 0.00400 = 0.000008 rather than 0.00800.

    Example code using only BigDecimal while avoiding Double. In Java syntax. Corrected your inputs.

    var a = "0.002000" ;
    var b = "0.004000" ;
    var expected = "0.000008" ;
    
    var actual = new BigDecimal( a ).multiply( new BigDecimal( b ) ) ;
    actual = actual.setScale( new BigDecimal( a ).scale() ) ;  // Set scale of result to match the scale of inputs.
    boolean matching = actual.equals( new BigDecimal( expected ) ) ;
    

    See this code run live at IdeOne.com.

    actual: actual: 0.000008

    matching: true