EN VI

Kotlin - Factorial calculation giving wrong answer with float or decimal number?

2024-03-17 01:30:07
How to Kotlin - Factorial calculation giving wrong answer with float or decimal number

this code is working fine to calculate factorial,but giving wrong answer with float or decimal number

    fun factorial(number: BigDecimal): BigDecimal {
        if (number >= BigDecimal(3000)) {
            is_infinity = true
            return BigDecimal.ZERO
        }
        return if (number < BigDecimal.ZERO) {
            domain_error = true
            BigDecimal.ZERO
        } else {
            val decimalPartOfNumber = number.toDouble() - number.toInt()
            if (decimalPartOfNumber == 0.0) {
                var factorial = BigInteger("1")
                for (i in 1..number.toInt()) {
                    factorial *= i.toBigInteger()
                }
                factorial.toBigDecimal()
            } else gammaLanczos(number + BigDecimal.ONE)
        }
    }

    private fun gammaLanczos(x: BigDecimal): BigDecimal {
        // https://rosettacode.org/wiki/Gamma_function
        var xx = x
        val p = doubleArrayOf(
            0.9999999999998099,
            676.5203681218851,
            -1259.1392167224028,
            771.3234287776531,
            -176.6150291621406,
            12.507343278686905,
            -0.13857109526572012,
            9.984369578019572E-6,
            1.5056327351493116e-7
        )
        val g = BigDecimal(7)
        if (xx < BigDecimal(0.5)) return (Math.PI / (sin(Math.PI * xx.toDouble()) * gammaLanczos(BigDecimal(1.0 - xx.toDouble())).toDouble())).toBigDecimal()
        xx--
        var a = p[0]
        val t = xx + g + BigDecimal(0.5)
        for (i in 1 until p.size) a += p[i] / (xx.toDouble() + i)
        return (sqrt(2.0 * Math.PI) * t.toDouble().pow(xx.toInt() + 0.5) * exp(-t.toDouble()) * a).toBigDecimal()
    }

For the code below, the test is failing after running nine the code

@Test
    fun sqrt() {

        val result =  NumberFormatter.factorial(BigDecimal(3.03))
        assertEquals(6.23120891259, result.toFloat())
    }

got this expected:<6.23120891259> but was:<5.8062997>

Could anyone help with this. Thanks

Solution:

The code has precision loss due to the conversion between BigDecimal and double. you can update the gammaLanczos to

fun gammaLanczos(x: BigDecimal): BigDecimal {
// Lanczos approximation parameters
   val p = arrayOf(
       676.5203681218851,
      -1259.1392167224028,
      771.3234287776531,
      -176.6150291621406,
      12.507343278686905,
      -0.13857109526572012,
      9.984369578019572e-6,
      1.5056327351493116e-7
  )
  val g = 7.0
  var z = x.toDouble() - 1.0

  var a = 0.99999999999980993
  for (i in p.indices) {
      a += p[i] / (z + i + 1)
  }

  val t = z + g + 0.5
  val sqrtTwoPi = sqrt(2.0 * PI)
  val firstPart = sqrtTwoPi * t.pow(z + 0.5) * exp(-t)
  val result = firstPart * a

  return BigDecimal(result, MathContext.DECIMAL64)
}

fun BigDecimal.pow(exponent: Double): BigDecimal {
   return this.toDouble().pow(exponent).toBigDecimal(MathContext.DECIMAL64)
}
Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login