JS の toFixed() メソッドの丸め精度の問題の詳細な説明

JS の toFixed() メソッドの丸め精度の問題の詳細な説明

落とし穴

最近、仕事で商品の割引価格を計算すると、いつも1セントの価格差が出てしまいます。お金が絡む問題はより敏感です。調査した結果、最終的にJSのネイティブtoFixedメソッドの問題であることが判明しました。

まあ、これはどういうルールですか? 。 。 (⊙お⊙)

充填方法

問題を急いで調べないでください。問題を見つけたので、まずはバグを修正してください。ネイティブ メソッドが機能しない場合は、自分で書いてください。数分しかかかりません、ハハハ!

/**
 * 小数点以下の桁数を保持し、自動的にゼロを埋め、切り上げます* @param num: 値* @param digit: 小数点以下の桁数* @returns 文字列
 */ 
関数 myFixed(数値, 桁数) {
  if(Object.is(parseFloat(num), NaN)) {
    return console.log(`受信値: ${num} は数値ではありません`);
  }
  num = parseFloat(num);
  (Math.round((num + Number.EPSILON) * Math.pow(10, digit)) / Math.pow(10, digit)).toFixed(digit) を返します。
}

何の穴ですか?

さて、バグが修正されたので、toFixed の秘密を探ってみましょう。

えーっと…まずは、えーっと…百度で、百度プログラミングエンジニアを対象に検索してみましょう。案の定、たくさんの結果が出てきます。典型的な問題のようです。

少し理解してみると、toFixed メソッドで使用される丸めは、私たちが理解している文字通りの丸めではないことがわかりました。 toFixed メソッドは、「偶数への丸め」と呼ばれる奇妙な方法を使用します。これは銀行家のアルゴリズムとしても知られています。これはどういう意味ですか?

完全な文: 「最も近い 5 に切り上げます。5 の後の数が 0 でない場合は、1 を加えます。5 の後の数が 0 の場合は、それが奇数か偶数かを検討します。5 の前の数が偶数の場合は、それを破棄します。5 の前の数が奇数の場合は、1 を加えます。」

一般的な意味は、捨てられた数字の値が ≤4 の場合は捨て、≥6 の場合は加算し、=5 の場合は 5 の後の数字に応じて決定し、5 の後に 0 以外の数字がある場合は 5 を 1 に丸め、5 の後に有効な数字がない場合は、2 つのケースに分けられます。5 の前の数字が偶数の場合は 5 を丸めて加算せず、5 の前の数字が奇数の場合は 5 を 1 に丸めます。

このルールに従って、ブラウザでさらにいくつかのテストを実行しましたが、まだ適切ではないと感じました。

// 5 の前の数字は偶数なので、切り捨てられませんか?
console.log(1.00000065.toFixed(7)); // 1.0000007 エラー console.log(1.000000065.toFixed(8)); // 1.00000007 エラー // 5 の前の数字は奇数なので、1 増加しませんか?
console.log(1.00000015.toFixed(7)); // 1.0000001 エラー console.log(1.000000015.toFixed(8)); // 1.00000001 エラー

これはなぜでしょうか?本当に混乱します。 。 。 (︶︿︶)

さらに調査を進めた結果、ようやく結果が得られました。では、このメソッドの ECMAScript 仕様の定義を見てみましょう。仕様に戻るのが最も信頼できる方法である場合もあります。

上の図はtoFixedメソッド全体の定義ですが、翻訳版です。多少の違いはありますが、大きな違いはありません。上のリンクをクリックして元のテキストを表示することもできます。図の赤いボックス部分に主に焦点を当て、数式を使用して破棄された値を計算します。

次の 2 つの例を取り上げ、テストしてみましょう。

console.log(1.0000005.toFixed(6)); // 1.000001 正解 console.log(1.00000005.toFixed(7)); // 1.0000000 間違い

まず、赤いボックスの条件によれば、x<10^21、1.0000005、1.00000005 はどちらも 10^21 未満なので、式 n / 10^ - x を直接使用できます。

まず、x=1.0000005 を式に代入して状況を確認しましょう。

// n1と仮定
var n1 = 1000000;
var x = 1.0000005;
var f = 6;
console.log((n1 / Math.pow(10, f) - x)); // -5.00000000069889e-7

// n2と仮定
var n2 = 1000001;
var x = 1.0000005;
var f = 6;
console.log((n2 / Math.pow(10, f) - x)); // 4.9999999998478444e-7

結果から、n1=1000001 の場合、結果は 0 に最も近い値であることがわかります。

console.log(1.0000005.toFixed(6)); // 1.000001が正しい

式に x=1.00000005 を代入してもう一度試してみましょう。

// n1と仮定
var n1 = 10000000;
var x = 1.00000005;
var f = 7;
console.log((n1 / Math.pow(10,f) - x)); // -4.99999999918171056e-8

// n2と仮定
var n2 = 10000001;
var x = 1.00000005;
var f = 7;
console.log((n2 / Math.pow(10,f) - x)); // 5.000000014021566e-8

結果から、n2=10000001 の場合、結果は 0 に最も近い値であることがわかります。

console.log(1.00000005.toFixed(7)); // 1.0000000 エラー

ああ...ここに来て、自分が大きな穴を掘ってしまったことに今気づきました。なぜこんなにたくさんのゼロを使わなければならないのでしょうか。数えると目が回ります。 。 。

一般的に、上記の例は、仕様で定義されている式を使用して結果を計算する方法を説明するだけです。仕様を理解できる場合は、直接代入しても問題ありません。

要約する

JS の toFixed() メソッドの丸め精度問題についての記事はこれで終わりです。JS toFixed() の丸め精度問題についての詳細は、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • JavaScript 正規表現における括弧の落とし穴の一覧
  • JS で tofixed と round を使用してデータの丸めを処理する場合の違い
  • JS はデータの丸め処理を行います (tofixed と round の違いの詳細な説明)
  • JavaScript で toFixed() 丸めを使用する方法の詳細な説明
  • js 正規表現の簡単な検証方法
  • jsは正規表現を使用して年、月、日の例をフィルタリングします
  • JavaScript 正規表現の説明
  • jmeter でインターフェース関連付けを実装する 2 つの方法 (正規表現抽出器と json 抽出器)
  • nest.js で正規表現を使用して検証を正しく設定する方法
  • jJavaScript における toFixed() と正規表現の落とし穴

<<:  ウェブページの表の分割線を削除する方法

>>:  MySQL全文インデックスの原理と欠点

推薦する

Reactにおけるコンテキスト適用シナリオの分析

コンテキストの定義と目的コンテキストは、コンポーネント ツリーにプロパティを明示的に渡すことなく、コ...

すべてのブラウザとの完全な互換性を実現するために最適なプリセットを選択してください

各ブラウザの select タグのプロパティと各ブラウザのサポートが多少異なるため、各ブラウザでの選...

ポートマッピング後に Docker コンテナが突然接続に失敗する問題のトラブルシューティング プロセス

1. 背景通常、外部サービスを提供する必要がある Docker コンテナの場合、起動時に -p コマ...

JSはGMTとUTCのタイムゾーンを完全に理解しています

目次序文1. GMT GMTとはGMTの歴史2. UTC UTCとはUTC は次の 2 つの部分で構...

DockerはPruneコマンドを使用してnoneイメージをクリーンアップします

目次無イメージの創造と混乱Noneオブジェクトをクリーンアップする方法トリムミラーコンテナで使用され...

Webデザインチュートリアル(7):Webデザインの効率化

<br />前の記事:Webデザインチュートリアル(6):デザインへの情熱を持ち続けまし...

標準のMySQL (x64) Windowsバージョンのインストール手順の詳細な説明

MySQL x64 はインストーラーを提供していません、インストーラーを提供していません、インストー...

Vueはシンプルな虫眼鏡効果を実装します

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

JavaScript キャンバス テキスト クロック

この記事では、テキストクロックを実装するためのキャンバスの具体的なコードを例として紹介します。具体的...

ノードの対応するバージョンに関する簡単な説明 node-sass sass-loader

目次ノードのバージョンが一致しない、ノードをアップグレードまたはダウングレードするnvm を使用して...

MySQL 5.7 でルートパスワードを忘れた後に変更する方法の詳細なチュートリアル

序文長い間、MySQL のアプリケーションおよび学習環境は MySQL 5.6 以前のバージョンであ...

JavaScript でシンプルな Web 時計を実装する

JavaScript を使用して Web ページ クロックを実装します。効果は次の図に示されています...

HTMLからReactを実装する方法を教えます

ReactとはReact は、効率的で高速なユーザー インターフェイスを構築するためのシンプルな J...

Windows 64 ビットに MySQL を再インストールするチュートリアル (Zip バージョン、解凍バージョンの MySQL インストール)

MySQLをアンインストールする1. コントロールパネルで、MySQLのすべてのコンポーネントをア...