ionspin / kotlin-multiplatform-bignum

A Kotlin multiplatform library for arbitrary precision arithmetics
Apache License 2.0
345 stars 41 forks source link

Convert BigDecimal to Double by "doubleValue()" method, get a wrong result #229

Closed JesseWo closed 2 years ago

JesseWo commented 2 years ago

Describe the bug

Unit Test:

  @Test
  fun testToDouble() {
    val result = "29514.9598393574297189".toBigDecimal().doubleValue()
    assertEquals(29514.9598393574297189, result)
  }

Output

expected:<29514.95983935743> but was:<0.1693214221471333>
Expected :29514.95983935743
Actual   :0.1693214221471333

To Reproduce Steps to reproduce the behavior:

  1. Operation
  2. Values to reproduce issue

Expected behavior A clear and concise description of what you expected to happen.

Platform

If JS (please complete the following information):

If Smartphone (please complete the following information):

Additional context Add any other context about the problem here.

ionspin commented 2 years ago

Hi @JesseWo which version of the library are you using?

There are several problems with your reproducer test:

  1. There is a warning from the IDE that 29514.9598393574297189 cannot be represented with required precision.
  2. Calling doubleValue() on big decimal that cannot be represented by a double correctly fails with java.lang.ArithmeticException: Value cannot be narrowed to float

With that said if you explicitly allow narrowing that loses precision the actual double is as you reported it and I will look into this.

JesseWo commented 2 years ago

lib version: 0.3.3

  1. I have not come across this warning from my Android Studio Chipmunk | 2021.2.1

In addition, the above unit test can be passed if it is reduced by one digit, e.g. 29514.959839357429718 Thanks for your response!

ionspin commented 2 years ago

Hi @JesseWo, I've fixed this issue and the fix will be in build 0.3.6 that is currently rolling out. There was a API change in version 0.3.4 that made doubleValue() explicitly require that the exact result is returned, unless you specify that you don't need exact result with doubleValue(false). The reason why we are now requiring is that a lot of people are unaware of the limitations in IEEE 754 double/float (which java and kotlin use)

A simple example would be to run this and observe it finishes successfully even though the numbers are different:

assertEquals(29514.9598393574297189, 29514.9598393574297194)