Kotlin 使用 BigDecimal 与 Double 进行计算

Kotlin Calculating with BigDecimal vs Double

我有两个函数。一个使用 BigIntegerBigDecimal。我想使用泰勒级数计算 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,Sin(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

我错过了什么?为什么 Double-Variant 给出了正确的值,而 BigDecimal 却没有?即使有 1000 次迭代。 注释掉的代码也是用于计算 Cos 的,但想先弄清楚这个问题,所以我让两个函数看起来一样

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)不是您想要的。


顺便说一句 - 我假设性能不是练习的一部分,否则你的阶乘实现是一个容易实现的目标。