Kotlin 大十进制除法返回不准确的结果

发布于 2025-01-15 15:05:21 字数 6678 浏览 2 评论 0原文

我正在使用 Kotlin 和 android studio 构建一个计算器(我的第一个项目)。在最近对十进制格式进行调整之前,所有方程都工作正常。现在,大型除法运算会返回不准确的结果。

例如 99,999,999 / 9% (0.09) = 1.11111110000E9 但是 9% / 99,999,999 = 0 当它应该 = 9.0000000009E-10

这是因为当前的 DecimalFormat 无法返回负指数吗?

编辑:经过更多测试,我发现将除法方法更改为

doubleNum = doubleNum.divide(numBigDecimal, 10, BigDecimal.ROUND_HALF_UP).stripTrailingZeros()

运行相同的方程将在格式化之前返回 9E-10 。十进制格式后结果显示为“.”仅不含数字

// enum class and class properties
enum class LogicTypes {
    None,Add,Subtract,Multiply,Divide
}

class MainActivity : AppCompatActivity() {

    private var logicActive = false
    private var currentLogic = LogicTypes.None
    private var currentNum = BigDecimal.ZERO
    private var displayNum = ""
    private var memoryNum = BigDecimal.ZERO
// add num function - buttons 0-9 send indices to num arg
fun addNum(num: BigDecimal) {
        val inputNum = num.toString()
        if (displayNum == "0" && inputNum == "0") {
            return
        }
        if (displayNum.contains(".")) {
            val stringForm = displayNum.substring(displayNum.indexOf('.'), displayNum.length)
            if (stringForm.length > 10) {
                clearCalc()
                Toast.makeText(this, "Only 10 digits after decimal point allowed.", Toast.LENGTH_SHORT).show()
                return
            }
        }
        if (displayNum.length >= 15 && !displayNum.contains(".")) {
            clearCalc()
            Toast.makeText(this, "Maximum of 15 digits allowed.", Toast.LENGTH_SHORT).show()
            return
        }
        if (inputNum == "0" && currentNum.toDouble() == 0.0 && displayNum.contains(".")) {
            if (displayNum.length > 11) {
                clearCalc()
                Toast.makeText(this, "Only 10 digits after decimal point allowed.", Toast.LENGTH_SHORT).show()
                return
            }
            displayNum = "$displayNum$inputNum"
            textView.text = displayNum
            return
        }
        if (logicActive) {
            logicActive = false
            displayNum = "0"
        }

        displayNum = "$displayNum$inputNum"
        updateDisplayNum()
    }
// set currentNum and send to numFormat function to update textView
fun updateDisplayNum() {
        if (currentNum.toString().length > 15) {
            clearCalc()
            Toast.makeText(this, "Maximum of 15 digits allowed.", Toast.LENGTH_SHORT).show()
            return
        }

        val numBigDecimal = displayNum.toBigDecimal()

        if(currentLogic == LogicTypes.None) {
            if(displayNum.contains("-") && currentNum == BigDecimal.ZERO) {
                textView.text = displayNum
                return
            } else {
                currentNum = numBigDecimal
            }
        }

        numFormat()
    }
// format decimal and integers and update textview with exponent
fun numFormat() {
        val numBigDecimal = displayNum.toBigDecimal()
        if(displayNum.contains(".")) {
            val stringForm = displayNum.substring(displayNum.indexOf('.'), displayNum.length)
            var numFormat = "#,##0."

            if(stringForm.length > 1) {
                for (num in stringForm.indices-1) {
                    numFormat += "0"
                }
            }

            if (displayNum.length > 16 || stringForm.length > 9) {
                // stringform length > 9 works for division result - anything higher returns trailing zeros.
                    // anything under 11 for stringform condition results in inaccurate input -
                        // adding decimal through addNum() will return Exponential notation before logic
                            // I only want E notation on result only- have yet to test other equations - 
                                // this can also make it impossible to take the result and conduct another logic operation as the result appends E0 
                                  // and thus the trailing digits after decimal is greater than 10
                    numFormat = "0.0000000000E0"
            }

            val df = DecimalFormat(numFormat)
            textView.text = df.format(numBigDecimal)
            return
        }
        var df = DecimalFormat("#,###")

        if (displayNum.length > 15) {
            df = DecimalFormat("0.0000000000E0")
        }

        textView.text = df.format(numBigDecimal)
    }
// change logic to enum mode when button operator pressed
fun changeLogic(mode: LogicTypes) {
        currentLogic = mode
        logicActive = true
    }

// calculate function
fun calculate() {
        if (logicActive || currentLogic == LogicTypes.Divide && displayNum.toBigDecimal() == BigDecimal.ZERO
            || currentNum == BigDecimal.ZERO) {
            Log.i(LOG_TAG, "caught the zero")
            return
        }
        val numBigDecimal = displayNum.toBigDecimal()
        var doubleNum = currentNum


        val currentNumString = doubleNum.stripTrailingZeros().toPlainString()
        val numBigDecimalString = numBigDecimal.stripTrailingZeros().toPlainString()

        val addMsg = getString(R.string.calc_message, currentNumString, "+", numBigDecimalString)
        val subMsg = getString(R.string.calc_message, currentNumString, "-", numBigDecimalString)
        val multiMsg = getString(R.string.calc_message, currentNumString, "*", numBigDecimalString)
        val divMsg = getString(R.string.calc_message, currentNumString, "/", numBigDecimalString)


        when(currentLogic) {
            LogicTypes.Add -> {
                hintView.text = addMsg
                doubleNum += numBigDecimal
                doubleNum = doubleNum.stripTrailingZeros()
            }
            LogicTypes.Subtract -> {
                hintView.text = subMsg
                doubleNum -= numBigDecimal
                doubleNum = doubleNum.stripTrailingZeros()
            }
            LogicTypes.Multiply -> {
                hintView.text = multiMsg
                doubleNum *= numBigDecimal
                doubleNum = doubleNum.stripTrailingZeros()
            }
            LogicTypes.Divide -> {
                hintView.text = divMsg
                doubleNum /= numBigDecimal
                doubleNum = doubleNum.stripTrailingZeros()
            }
            LogicTypes.None -> return
        }

        currentLogic = LogicTypes.None
        displayNum = doubleNum.toString()
        updateDisplayNum()
        logicActive = true
    }

I am building a calculator with Kotlin and android studio (my first project). All equations were working properly until recent tweaks with decimal format. Now large division operations returns inaccurate results.

For example 99,999,999 / 9% (0.09) = 1.11111110000E9
But 9% / 99,999,999 = 0 when it should = 9.0000000009E-10

Is this because the current DecimalFormat cannot return the negative exponent?

EDIT: after more testing I've found that changing division method to

doubleNum = doubleNum.divide(numBigDecimal, 10, BigDecimal.ROUND_HALF_UP).stripTrailingZeros()

running the same equation will return 9E-10 before formatting. After decimal format the result shows as "." only with no digits

// enum class and class properties
enum class LogicTypes {
    None,Add,Subtract,Multiply,Divide
}

class MainActivity : AppCompatActivity() {

    private var logicActive = false
    private var currentLogic = LogicTypes.None
    private var currentNum = BigDecimal.ZERO
    private var displayNum = ""
    private var memoryNum = BigDecimal.ZERO
// add num function - buttons 0-9 send indices to num arg
fun addNum(num: BigDecimal) {
        val inputNum = num.toString()
        if (displayNum == "0" && inputNum == "0") {
            return
        }
        if (displayNum.contains(".")) {
            val stringForm = displayNum.substring(displayNum.indexOf('.'), displayNum.length)
            if (stringForm.length > 10) {
                clearCalc()
                Toast.makeText(this, "Only 10 digits after decimal point allowed.", Toast.LENGTH_SHORT).show()
                return
            }
        }
        if (displayNum.length >= 15 && !displayNum.contains(".")) {
            clearCalc()
            Toast.makeText(this, "Maximum of 15 digits allowed.", Toast.LENGTH_SHORT).show()
            return
        }
        if (inputNum == "0" && currentNum.toDouble() == 0.0 && displayNum.contains(".")) {
            if (displayNum.length > 11) {
                clearCalc()
                Toast.makeText(this, "Only 10 digits after decimal point allowed.", Toast.LENGTH_SHORT).show()
                return
            }
            displayNum = "$displayNum$inputNum"
            textView.text = displayNum
            return
        }
        if (logicActive) {
            logicActive = false
            displayNum = "0"
        }

        displayNum = "$displayNum$inputNum"
        updateDisplayNum()
    }
// set currentNum and send to numFormat function to update textView
fun updateDisplayNum() {
        if (currentNum.toString().length > 15) {
            clearCalc()
            Toast.makeText(this, "Maximum of 15 digits allowed.", Toast.LENGTH_SHORT).show()
            return
        }

        val numBigDecimal = displayNum.toBigDecimal()

        if(currentLogic == LogicTypes.None) {
            if(displayNum.contains("-") && currentNum == BigDecimal.ZERO) {
                textView.text = displayNum
                return
            } else {
                currentNum = numBigDecimal
            }
        }

        numFormat()
    }
// format decimal and integers and update textview with exponent
fun numFormat() {
        val numBigDecimal = displayNum.toBigDecimal()
        if(displayNum.contains(".")) {
            val stringForm = displayNum.substring(displayNum.indexOf('.'), displayNum.length)
            var numFormat = "#,##0."

            if(stringForm.length > 1) {
                for (num in stringForm.indices-1) {
                    numFormat += "0"
                }
            }

            if (displayNum.length > 16 || stringForm.length > 9) {
                // stringform length > 9 works for division result - anything higher returns trailing zeros.
                    // anything under 11 for stringform condition results in inaccurate input -
                        // adding decimal through addNum() will return Exponential notation before logic
                            // I only want E notation on result only- have yet to test other equations - 
                                // this can also make it impossible to take the result and conduct another logic operation as the result appends E0 
                                  // and thus the trailing digits after decimal is greater than 10
                    numFormat = "0.0000000000E0"
            }

            val df = DecimalFormat(numFormat)
            textView.text = df.format(numBigDecimal)
            return
        }
        var df = DecimalFormat("#,###")

        if (displayNum.length > 15) {
            df = DecimalFormat("0.0000000000E0")
        }

        textView.text = df.format(numBigDecimal)
    }
// change logic to enum mode when button operator pressed
fun changeLogic(mode: LogicTypes) {
        currentLogic = mode
        logicActive = true
    }

// calculate function
fun calculate() {
        if (logicActive || currentLogic == LogicTypes.Divide && displayNum.toBigDecimal() == BigDecimal.ZERO
            || currentNum == BigDecimal.ZERO) {
            Log.i(LOG_TAG, "caught the zero")
            return
        }
        val numBigDecimal = displayNum.toBigDecimal()
        var doubleNum = currentNum


        val currentNumString = doubleNum.stripTrailingZeros().toPlainString()
        val numBigDecimalString = numBigDecimal.stripTrailingZeros().toPlainString()

        val addMsg = getString(R.string.calc_message, currentNumString, "+", numBigDecimalString)
        val subMsg = getString(R.string.calc_message, currentNumString, "-", numBigDecimalString)
        val multiMsg = getString(R.string.calc_message, currentNumString, "*", numBigDecimalString)
        val divMsg = getString(R.string.calc_message, currentNumString, "/", numBigDecimalString)


        when(currentLogic) {
            LogicTypes.Add -> {
                hintView.text = addMsg
                doubleNum += numBigDecimal
                doubleNum = doubleNum.stripTrailingZeros()
            }
            LogicTypes.Subtract -> {
                hintView.text = subMsg
                doubleNum -= numBigDecimal
                doubleNum = doubleNum.stripTrailingZeros()
            }
            LogicTypes.Multiply -> {
                hintView.text = multiMsg
                doubleNum *= numBigDecimal
                doubleNum = doubleNum.stripTrailingZeros()
            }
            LogicTypes.Divide -> {
                hintView.text = divMsg
                doubleNum /= numBigDecimal
                doubleNum = doubleNum.stripTrailingZeros()
            }
            LogicTypes.None -> return
        }

        currentLogic = LogicTypes.None
        displayNum = doubleNum.toString()
        updateDisplayNum()
        logicActive = true
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

何以笙箫默 2025-01-22 15:05:21

好吧,问题是我在计算函数中使用了它。

displayNum = doubleNum.toString()

更改为 .toPlainString() 会给出正确的符号。格式仍然存在问题,但我会看看是否可以自己解决这些问题

编辑:我通过创建布尔属性并在计算中将其设置为 true 解决了 numFormat 中的格式问题函数,并将其传递给 numFormat 条件:

if (displayNum.length > 16 || stringForm.length > 9 && resultActive) {
      numFormat = "0.0000000000E0"
      resultActive = false
   }

这样格式仅适用于计算出的数字

我还将其传递给 addNum 函数以在第一次计算后进行计算

if(resultActive) {
      resultActive = false
   }

Ok the issue was that I was using this in the calculate function.

displayNum = doubleNum.toString()

Changing to .toPlainString() gives correct notations. There are still issues with formatting but I'll see if I can work those out on my own

EDIT: I solved the formatting issue in the numFormat by creating a boolean property, setting it to true in the calculation function, and passing it to the numFormat condition:

if (displayNum.length > 16 || stringForm.length > 9 && resultActive) {
      numFormat = "0.0000000000E0"
      resultActive = false
   }

This way the format only applies to calculated numbers

I also passed it to the addNum function for calculations made after the first calculation

if(resultActive) {
      resultActive = false
   }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文