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全文インデックスの原理と欠点

推薦する

Logrotate は 2 時間ごとに Catalina.out ログローテーションを実装します

1. Logrotateツールの紹介Logrotate はログファイル管理ツールです。Linux に...

ウェブインターフェースデザインでウェブサイトのスタイルガイドを作成する方法(画像とテキスト付き)

スタイル ガイドとは何でしょうか? 簡単に言えば、ストーリーを伝える方法を説明するドキュメントです。...

Javascript DOM、ノード、要素取得の紹介

目次DOMノード要素ノード:テキストノード:プロパティ ノード:要素を取得getElementByI...

CSS はモバイル デバイスで水平スクロール ナビゲーション バーを実装します (PC デバイスにも適用可能)

関数の起源最近、水平スクロール バーを必要とする H5 に取り組んでいました。いくつかのドキュメント...

Vueはechartsを使用して組織図を描画します

昨日、円形のプログレスバー (Vue 円形プログレスバーを参照してください) についてブログを書きま...

JavaScript データ型変換の例 (他の型を文字列、数値型、ブール型に変換する)

序文データ型変換とは何ですか?フォームまたはプロンプトを使用して取得されるデフォルトのデータ型は文字...

Docker execは複数のコマンドを実行します

docker exec コマンドは、実行中のコンテナ内でコマンドを実行できます。 docker ex...

HTML でのテキストエリアの使用と一般的な問題およびケース分析

textarea タグはよく使われる HTML タグです。主に長いテキストを入力するときに改行するた...

CSS3は、変換変形とイベントを組み合わせて扇形のナビゲーションを完成させます。

この場合、transition という単語を間違って書いたため、午後中ずっとそれに取り組みました。本...

フロントエンドエンジニアが作ったクールなインタラクティブウェブサイトを推薦します

ウェブサイトリンク: http://strml.net/サミュエル・リード著ヒント: 昨日、Mome...

Dockerサーバーのストレージリソースプール不足問題の解決

目次1. 問題の説明2. 問題分析3. 問題解決1. Dockerのディスク使用量を確認する2. 再...

階段を転がす特殊効果を実現する JavaScript (jQuery 実装)

皆さんもJDを使ったことがあると思います。ホームページには非常によく見られる機能があります。階段の特...

あなたを救うために、私のテーブルは何を使えばいいでしょうか (Haiyu Blog)

テーブルはかつて、Web ページの開発、つまりレイアウトにおいて非常に重要な役割を果たしていました。...

CSSのclip-pathプロパティを使用して不規則なグラフィックを表示する

clip-path CSS プロパティはクリッピングを使用して要素の表示可能領域を作成します。領域内...

CentOS8.0 で FTP サーバーをインストールして設定する方法

CentOS8.0-1905 のリリース後、FTP サーバーを CentOS の新しいバージョンに移...