亚洲乱色熟女一区二区三区丝袜,天堂√中文最新版在线,亚洲精品乱码久久久久久蜜桃图片,香蕉久久久久久av成人,欧美丰满熟妇bbb久久久

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

js計算精度溢出,自定義加減乘除類

freeflydom
2025年9月10日 10:29 本文熱度 160

前言

眾所周知,javascript因為底層原因存在計算精度問題,這里就不做過多贅述;如果不理解,可以先去看看我以前寫的文章《js數(shù)學(xué)計算精度溢出問題》


優(yōu)缺點

優(yōu)點

  • 計算流程有日志可以追溯

  • 鏈?zhǔn)秸{(diào)用,方法可接受多參數(shù)

  • 接受數(shù)學(xué)表達(dá)式字符串

  • 計算精度可調(diào)節(jié)

  • 代碼量小

  • ts/js版本

缺點

  • 只能夠?qū)崿F(xiàn)加減乘除的運算,對于其他復(fù)雜的運算,需要自己去加

核心代碼

1)鏈?zhǔn)秸{(diào)用,核心方法

  // 初始化獲取操作代理
    get operationProxy() {
        const proxy = {
            'add': CusMath.accAdd,
            'sub': CusMath.accSub,
            'mul': CusMath.accMul,
            'div': CusMath.accDiv,
        }
        return proxy
    }
    // 操作方法
    operation(type, ...args) {
        const that = this
        const operaFun = this.operationProxy[type]
        if (typeof operaFun === 'function' && that.isNotEmptyArr(args)) {
            this._processValue = args.reduce(function (a, b) {
                const val = operaFun(that.convertNumber(a), that.convertNumber(b))
                that.logPush({
                    operationName: operaFun?.name,
                    result: val,
                    type: 'chain',
                    params: [a, b]
                })
                return val
            }, this._processValue)
        }
        return this
    }

2)數(shù)學(xué)表達(dá)式計算核心方法

    // 表達(dá)式
    expression(expStr) {
        if (expStr && typeof expStr === 'string') {
            // 優(yōu)先級,括號 > 乘除 > 加減
            const bool = this.validateExpression(expStr)
            if (bool) {
                this._original_expression = expStr
                this._process_expression = expStr
                this.expLogPush(this._process_expression)
                try {
                    const result = this.dealAndCalcFirstBracketArr()
                    this.expLogPush(result)
                    return result
                } catch (err) {
                    console.log(err?.message)
                }
            } else {
                throw new Error('無效數(shù)學(xué)表達(dá)式')
            }
        } else {
            throw new Error('數(shù)學(xué)表達(dá)式為字符串且不能為空')
        }
    }
    // 獲取一級括號的計算表達(dá)式
    getFirstBracketExpress(newStr) {
        console.log('newStr=====>', newStr)
        const nStr = String(newStr)
        let matches = []
        if (nStr.indexOf('(') !== -1 && nStr.indexOf(')') !== -1) {
            const regex = /\(([^()]+)\)/g;
            matches = [...(newStr?.matchAll(regex) || [])].map(match => match[1]);
        }
        return matches
    }
    // 處理并計算一級括號的計算表達(dá)式
    dealAndCalcFirstBracketArr() {
        let val = 0
        const bracketArr = this.getFirstBracketExpress(this._process_expression)
        if (this.isNotEmptyArr(bracketArr)) {
            console.log('解析括號數(shù)組', bracketArr)
            for (let i = 0; i < bracketArr.length; i++) {
                const bracketExpStr = bracketArr[i]
                const result_val = this.calcFlatExpress(bracketExpStr)
                // 將括號內(nèi)的表達(dá)式替換成最終計算的結(jié)果
                const regStr = '(' + bracketExpStr + ')'
                this._process_expression = this._process_expression.replace(regStr, result_val)
                this.expLogPush(this._process_expression)
                console.log(`括號字符串【${regStr}】替換為【${result_val}】得到新表達(dá)式為【${this._process_expression}】`)
            }
            // 遞歸循環(huán)去掉多層級的括號
            return this.dealAndCalcFirstBracketArr()
        } else {
            this.expLogPush(this._process_expression)
            val = this.calcFlatExpress(this._process_expression)
        }
        return val
    }
    // 計算平鋪的表達(dá)式
    calcFlatExpress(expStr) {
        console.log('計算平鋪的表達(dá)式', expStr)
        // 轉(zhuǎn)換操作, +- => - ;  ++ => + ; -- => + ;  -+ => -
        const dealExpStr = expStr?.replace(/\s/g, '')?.replace(/\+\-/g, '-')?.replace(/\+\+/g, '+')?.replace(/\-\-/g, '+')?.replace(/\-\+/g, '-')
        // 使用正則表達(dá)式匹配所有數(shù)字和運算符
        const parts = dealExpStr.split(/([+-])/);
        // 挑出乘除的字符串
        let mulOrDivArr = []
        const cloneParts = JSON.parse(JSON.stringify(parts))
        cloneParts.forEach((ele, index) => {
            if (!['+', '-'].includes(ele) && Number.isNaN(Number(ele))) {
                mulOrDivArr.push({
                    ele,
                    index
                })
            }
        })
        let val = 0
        if (mulOrDivArr?.length) {
            for (let i = 0; i < mulOrDivArr.length; i++) {
                const { ele: expStr, index } = mulOrDivArr[i]
                const result = this.getFlatExpressResult(expStr)
                console.log('result', result)
                cloneParts.splice(index, 1, result)
            }
        }
        if (cloneParts?.length > 1) {
            const finalExpStr = cloneParts.join('')
            val = this.getFlatExpressResult(finalExpStr)
        } else {
            val = cloneParts[0] || 0
        }
        return val
    }
    // 無優(yōu)先級的,從左到右計算,執(zhí)行計算并返回計算結(jié)果
    getFlatExpressResult(expStr) {
        const parts = this.getExpParts(expStr)
        console.log('運算符拆解', parts)
        let val = 0
        if (this.isNotEmptyArr(parts)) {
            const expressKeyArr = Object.keys(this.expressionOperationProxy)
            const numArr = []
            const operaArr = []
            // 奇數(shù)是數(shù)字,偶數(shù)是計算操作符,
            for (let i = 0; i < parts.length; i++) {
                const ele = parts[i]
                const isOdd = this.isOddNumber(i + 1)
                if (isOdd) {
                    const isNum = this.isNumber(ele)
                    if (isNum) {
                        numArr.push(ele)
                    } else {
                        throw new Error(`存在非數(shù)字項【${ele}】`)
                    }
                } else {
                    if (expressKeyArr.includes(ele) && !isOdd) {
                        const operaFun = this.expressionOperationProxy[ele]
                        if (typeof operaFun === 'function') {
                            operaArr.push({
                                operationName: operaFun?.name,
                                operaFun,
                                index: i,
                            })
                        } else {
                            throw new Error(`expressionOperationProxy對象解析【${ele}】不是一個方法`)
                        }
                    } else {
                        throw new Error(`存在非數(shù)學(xué)運算符【${ele}】`)
                    }
                }
            }
            const operaArrLen = operaArr.length
            // 操作符不能為空, 如果為空則賦值第一個, 操作符相比于數(shù)字始終要少一個
            if (this.isNotEmptyArr(operaArr) && operaArr.length === numArr.length - 1) {
                while (operaArr.length) {
                    const arg1 = operaArrLen === operaArr.length ? numArr.shift() : val
                    const operaFunObj = operaArr.shift()
                    const arg2 = numArr.shift()
                    val = operaFunObj.operaFun(arg1, arg2)
                }
            } else {
                throw new Error(`邏輯錯誤`)
            }
        }
        return val
    }
    // 拆解運算表達(dá)式
    getExpParts(expStr) {
        const parts = expStr.match(/([0-9]+(\.[0-9]+)?)|([+\-*\/])/g);
        return parts || []
    }
    // 是否是表達(dá)式
    validateExpression(str) {
        const mulReg = /^([^*]|\*[^*])*$/
        const divReg = /^([^/]|\/[^/])*$/;
        const newStr = str.replace(/\s/g, '')
        // 不能出現(xiàn)連續(xù)的 ** 或者 // ,只能單個
        const mulBool = mulReg.test(newStr)
        const divBool = divReg.test(newStr)
        // 不能包含以下的其他特殊字符
        const rest = newStr.replace(/\d+|\+|\-|\*|\/|\.|\(|\)/g, '')
        return mulBool && divBool && !Boolean(rest)
    }

上述代碼只是核心代碼的簡單介紹,詳細(xì)請看源碼

五、使用CusMath類

import { CusMath } from "./cusMath.js";
const cusMath = new CusMath()
// 第一種方式
const calcInstance = cusMath.operation('add', 3.251, 253.635, 1.25, 42).operation('sub', 63.652, 4.952)
const totalLogs = calcInstance._logs
const totalVal = calcInstance.end()
// console.log('totalLogs=====>', totalLogs) // 方法鏈?zhǔn)秸{(diào)用日志
console.log('totalVal=====>', totalVal)
// 第二種方式
const otherInstance = cusMath.add(3.251, 253.635, 1.25, 42).sub(63.652, 4.952)
const otherLogs = otherInstance._logs
const otherVal = otherInstance.end()
// console.log('otherLogs=====>', otherLogs) // 方法鏈?zhǔn)秸{(diào)用日志
console.log('otherVal=====>', otherVal)
// 第三種方式,接受表達(dá)式
const str = '((20.124 * 2.35 / 5.96 + 20.124 * 65 / 3 - 20.124 + 2.35 * 5.96) / 2.36) * 3.241 + 2.53 * 5.96'
// const str = '5 * 2 - 3 + 5 * 6 / 3 + 6 - 8 + 41'
// const str = '5 * 2 - (3 * (5 * 6 / 3 + 6) ) * 12 - 8 + 41'
// const str = '0.1+0.2'
const result = cusMath.expression(str)
console.log('resultexpression=====>', result)
const logs = cusMath._logs
console.log('運算公式演變logs=====>', logs)

六、源碼地址

github: github.com/ArcherNull/…


該文章在 2025/9/10 10:32:43 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點晴ERP是一款針對中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內(nèi)大量中小企業(yè)的青睞。
點晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運作、調(diào)度、堆場、車隊、財務(wù)費用、相關(guān)報表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點,圍繞調(diào)度、堆場作業(yè)而開發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點晴WMS倉儲管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務(wù)都免費,不限功能、不限時間、不限用戶的免費OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved