Котлин Вычисляет с помощью BigDecimal против Double

#kotlin #trigonometry #biginteger #bigdecimal

Вопрос:

У меня есть 2 функции. Один использует BigInteger и BigDecimal . Я хочу рассчитать sin(z) , используя ряд Тейлора: Серия грехов

Вот мой код:

 fun sinus(z: BigDecimal, upperBound: Int = 100): BigDecimal = calcSin(z, upperBound)

fun cosinus(z: BigDecimal, upperBound: Int = 100): BigDecimal = calcSin(z, upperBound, false)

fun calcSin(z: BigDecimal, upperBound: Int = 100, isSin: Boolean = true): BigDecimal {
    var erg: BigDecimal = BigDecimal.ZERO
    for (n in 0..upperBound) {
//        val zaehler = (-1.0).pow(n).toBigDecimal() * z.pow(2 * n   (if (isSin) 1 else 0))
//        val nenner = fac(2 * n   (if (isSin) 1 else 0)).toBigDecimal()
        val zaehler = (-1.0).pow(n).toBigDecimal() * z.pow(2 * n   1)
        val nenner = fac(2 * n   1).toBigDecimal()
        erg  = (zaehler / nenner)
    }
    return erg
}

fun calcSin(z: Double, upperBound: Int = 100): Double {
    var res = 0.0
    for (n in 0..upperBound) {
        val zaehler = (-1.0).pow(n) * z.pow(2 * n   1)
        val nenner = fac(2 * n   1, true)
        res  = (zaehler / nenner)
    }
    return res
}

fun fac(n: Int): BigInteger = if (n == 0 || n == 1) BigInteger.ONE else n.toBigInteger() * fac(n - 1)

fun fac(n: Int, dummy: Boolean): Double = if (n == 0 || n == 1) 1.0 else n.toDouble() * fac(n - 1, dummy)
 

Согласно Google, Грех(1) — это

0.8414709848

Однако результатом следующего является:

 println("Sinus 1: ${sinus(1.0.toBigDecimal())}")
println("Sinus 1: ${sinus(1.0.toBigDecimal()).toDouble()}")
println("Sinus 1: ${sinus(1.0.toBigDecimal(), 1000)}")
println("Sinus 1: ${calcSin(1.0)}")
 

Выход:

 Sinus 1: 0.8414373208078281027995610599000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Sinus 1: 0.8414373208078281
Sinus 1: 0.8414373208078281027995610599000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Sinus 1: 0.8414709848078965
 

Что я упускаю? Почему Двойной вариант дает правильное значение, в то время как двоичный-нет? Даже с 1000 итерациями.
Закомментированный код также предназначался для расчета Cos, но сначала я хотел разобраться с этой проблемой, поэтому я сделал так, чтобы обе функции выглядели одинаково

Ответ №1:

В этом BigDecimal варианте попробуйте заменить erg = (zaehler / nenner) на erg = (zaehler.divide(nenner, 20, RoundingMode.HALF_EVEN))

Я подозреваю, что значения по умолчанию для масштабирования результатов деления (как описано здесь https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/math/BigDecimal.html) не то, что вы хотите.


Кстати, я предполагаю, что производительность не является частью упражнения, иначе ваша реализация факториала — это низко висящий плод.

Комментарии:

1. Я знаю, что это не идеально реализовано, даже Грех. Я имел в виду лучшую реализацию с точки зрения производительности, но я только начинаю изучать Kotlin, и это всего лишь примеры для отработки синтаксиса по сравнению с Java