JS の精度外数値問題の解決

JS の精度外数値問題の解決

精度の問題に対する最もわかりやすい説明

たとえば、1÷3=0.33333333...という数字は、3が無限に繰り返されることは誰もが知っており、数学で表現することもできますが、コンピューターはそれを保存して、次回取り出して使用できるようにする必要があります。しかし、0.333333...この数字は無限に繰り返されますが、コンピューターにそれを保存させるにはどうすればよいでしょうか。コンピュータのメモリがどれだけ大きくても、すべてを保存することはできません。数学的な相対的な値を保存することはできず、おおよその値しか保存できないため、コンピュータがそれを保存してから使用するために取り出すと、精度の問題が発生します。

JSは数値解の精度を超える

1. js で安全に扱える最大の数値は Math.pow(2,53) - 1 です。数値がこれを超えると精度が失われます。次のように数値を文字列に変換することで解決できます。

// js 最大安全数: Math.pow(2, 53)-1

a = '123456444565456.889' とします
b = '121231456.32' とします
// a + b = '123456565796913.209'

関数 addTwo(a, b) {
    //1. 2つの数値の長さを比較し、短い方の数値の前に0を追加します
    (a.長さ > b.長さ) の場合 {
        arr = Array(a.length - b.length).fill(0); とします。
        b = arr.join('') + b
    } それ以外の場合 (a.length < b.length) {
        arr = Array(b.length - a.length).fill(0); とします。
        a = arr.join('') + a
    }

    //2. 2 つの数字を逆にします (これは、人々は左から右に加算することに慣れていますが、数字は右から左に加算されるため、逆にすると理解しやすくなるためです)
    a = a.split('').reverse();
    b = b.split('').reverse();

    //3. 2 つの配列をループして加算します。合計が 10 より大きい場合、符号 = 1 となり、現在の位置の値は (合計 % 10) になります。
    let sign = 0; // 繰り上がりがあるかどうかをマークする let newVal = []; // 最終結果を格納するために使用する for (let j = 0; j < a.length; j++) {
        let val = a[j] / 1 + b[j] / 1 + sign; // すべてが数値であることを確認するために 1 で割ります。ここで Number() を使用することもできます。
        (値> = 10)の場合{
            符号 = 1;
            newVal.unshift(val % 10) // ここでは push の代わりに unshift が使用されています。これにより、reverse を使用する必要がなくなります。
        } それ以外 {
            符号 = 0;
            newVal.unshift(val)
        }
    }

    // 最後の加算では数字 '1' を追加する必要があります
    戻り値 sign && newVal.unshift(sign) && newVal.join('') || newVal.join('')
}

// 他の友人の簡潔な書き方を参考にしてください function addTwo(a,b) {
    温度を0にする
    res = '' とします
    a = a.split('')
    b = b.split('')
    while(a.length || b.length || temp) {
        temp += 数値(a.pop() || 0) + 数値(b.pop() || 0)
        解像度 = (温度%10) + 解像度
        温度 = 温度 > 9
    }
    res.replace(/^0+/g, '') を返します
}

2. 小数部分を加算する場合は、上記のメソッドを 1 回カプセル化すると、完全な実装は次のようになります。

a = '123456444565456.889' とします
b = '121231456.32'とする
// a + b = '123456565796913.209'

関数 addTwo(a = '0',b = '0', isHasDecimal=false) {
    //1. 2つの数値の長さを比較し、短い方の数値の前に0を追加します
    (a.長さ > b.長さ) の場合 {
        arr = Array(a.length - b.length).fill(0); とします。
        b = isHasDecimal && (b + arr.join('')) || arr.join('') + b
    } そうでなければ (a.length < b.length) {
        arr = Array(b.length - a.length).fill(0); とします。
        a = isHasDecimal && (a + arr.join('')) || arr.join('') + a
    }

    //2. 2 つの数字を逆にします (これは、人々は左から右に加算することに慣れていますが、数字は右から左に加算されるため、逆にすると理解しやすくなるためです)
    a = a.split('').reverse();
    b = b.split('').reverse();


    //3. 2 つの配列をループして加算します。合計が 10 より大きい場合、符号 = 1 となり、現在の位置の値は (合計 % 10) になります。
    let sign = 0; // 繰り上がりがあるかどうかをマークする let newVal = []; // 最終結果を格納するために使用する for (let j = 0; j < a.length; j++) {
        let val = a[j] / 1 + b[j] / 1 + sign; // すべてが数値であることを確認するために 1 で割ります。ここで Number() を使用することもできます。
        (値> = 10)の場合{
            符号 = 1;
            newVal.unshift(val % 10) // ここでは push の代わりに unshift が使用されています。これにより、reverse を使用する必要がなくなります。
        } それ以外 {
            符号 = 0;
            newVal.unshift(val)
        }
    }

    // 最後の加算では数字 '1' を追加する必要があります
    戻り値 sign && newVal.unshift(sign) && newVal.join('') || newVal.join('')
}

関数 add(a,b) {
    num1 = String(a).split('.') とします。
    num2 = String(b).split('.') とします。
    intSum = addTwo(num1[0], num2[0]) とします。
    res = intSumとする

    num1の長さ>1 || num2の長さ>1の場合{
        小数点合計 = addTwo(num1[1], num2[1], true) とします。

        (decimalSum.length > (num1[1]||'0').length && DecimalSum.length > (num2[1]||'0').length) の場合 {
            intSum = addTwo(intSum、decimalSum[0]) です。
            小数点合計 = 小数点合計.スライス(1)
            res = `${intSum}.${decimalSum}`
        } それ以外 {
            res = `${intSum}.${decimalSum}`
        }
    }
    戻り値
}
コンソール.log(add(a, b)) // 123456565796913.209
// コンソール.log(add('325', '988')) // 1313

JSデジタル精度損失の典型的な問題を見てみましょう

// 追加 ========================
0.1 + 0.2 = 0.300000000000000004
0.7 + 0.1 = 0.7999999999999999
0.2 + 0.4 = 0.60000000000000001

// 減算 =======================
1.5 - 1.2 = 0.300000000000000004
0.3 - 0.2 = 0.09999999999999998

// 乗算 ========================
19.9 * 100 = 1989.99999999999998
0.8 * 3 = 2.40000000000000004
35.41 * 100 = 3540.99999999999995

// 除算 ========================
0.3 / 0.1 = 2.99999999999999996
0.69 / 10 = 0.06899999999999999

要約する

JS の精度外数値の問題を解決する方法についての記事はこれで終わりです。JS の精度外数値に関する関連コンテンツをさらにご覧になりたい場合は、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続きご覧ください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • JavaScript でデジタル計算の精度エラーを回避する方法の詳細な説明
  • JavaScript で数値精度が失われる問題を解決する方法
  • JSデジタル精度低下の原因分析と解決

<<:  Windows ベースの MySQL 8.0.12 のインストール

>>:  DockerでVueプロジェクトをデプロイする方法を教えます

推薦する

IDEA 2020.3.1 で Tomcat をデプロイし、最初の Web プロジェクトを作成するプロセスの詳細な説明

目次Tomcat の紹介Tomcat の展開Web プロジェクトの作成tomcatの設定プロジェクト...

MySql 8.0 と対応するドライバー パッケージの一致に関する注意事項

MySql 8.0 対応ドライバパッケージのマッチングMySql データベースをバージョン 8.0 ...

MySQLデータベース移行におけるデータ文字化けの問題を解決する

リーダーの指示のもと、Java プロジェクトを引き継ぎ、リファクタリングを行う必要がありました。同時...

WindowsにOpenSSLをインストールし、OpenSSLを使用して公開鍵と秘密鍵を生成します。

1. OpenSSL公式サイト公式ダウンロードアドレス: https://www.openssl....

du コマンドを使用して Linux システム ディレクトリのサイズを取得する方法

Linux システムを使用したことがある人なら、Linux システムの ls コマンドは通常、ファイ...

CSS と HTML とフロントエンド テクノロジーのレイヤー図

JavascriptとDOMの関係は非常に曖昧で、CSSやHTMLのフロントエンド技術層も理解してい...

GolangでMySQLデータベースのバックアップを実装する方法

背景Navicat は、最高の MySQL 視覚化ツールです。ただし、ビューのインポートとエクスポー...

虫眼鏡効果を実現するJavaScript

この記事では、虫眼鏡効果を実現するためのJavaScriptの具体的なコードを参考までに紹介します。...

Xmeter APIインターフェーステストツールの使用状況の分析

XMeter API は、以下のサービスを含む、JMeter に基づくワンストップのオンライン イン...

Dockerがログファイルを保存する場所の詳細な説明

目次ログはどこに保存されますか?コンテナ内のアプリケーションからのログを表示するDockerデーモン...

歴史的な Linux 画像処理および修復ソリューション

従来の Linux イメージで作成された ECS クラウド サーバーには、NTP と YUM が設定...

Vueはシンプルな計算機能を実装します

この記事では、参考までに、Vue の具体的なコードで簡単な計算機を実装する方法を紹介します。具体的な...

vscodeで保存した後のHTML自動フォーマットの問題を解決する

vsCode のバージョンは最近更新され、現在のバージョン番号は 1.43 です。実際、vsCode...

ろうそくを溶かす(水滴)サンプルコードを実現する純粋な CSS

成果を達成する実装のアイデアフィルターのコントラストとぼかしを利用して溶ける効果を実現します。親要素...

Ubuntu システムに Theano と Keras をインストールする方法

注: システムは Ubuntu 14.04LTS、32 ビット オペレーティング システムです。Py...