web-dev-qa-db-ja.com

Math.round(num)vs num.toFixed(0)およびブラウザーの不整合

次のコードを検討してください。

_for (var i=0;i<3;i++){
   var num = i + 0.50;
   var output = num + " " + Math.round(num) + " " + num.toFixed(0);
   alert(output);
}
_

でOpera 9.63私は得る:

0.5 1

1.5 2 2

2.5 3 2

FF 3.03では次のようになります:

0.5 1 1

1.5 2 2

2.5 3

In IE 7私は得る:

0.5 1

1.5 2 2

2.5 3

太字の結果に注意してください。なぜこの矛盾が存在するのですか?これはtoFixed(0)を避けるべきだということですか?数値を最も近い整数に丸める正しい方法は何ですか?

50
eft

編集:編集に回答するには、_Math.round_を使用します。また、Numberオブジェクトのプロトタイプを作成して、その構文を希望する場合に入札させることもできます。

_Number.prototype.round = function() {
  return Math.round(this);
}
var num = 3.5;
alert(num.round())
_

私はこれまでNumber.toFixed()を使用したことはありません(ほとんどのJSライブラリが toInt() メソッドを提供しているためです)が、結果から判断すると、一貫したMathメソッド(roundfloorceil)を使用し、ブラウザ間の一貫性が目的の場合はtoFixedために。

33
tj111

FFはtoFixedで正しいことをしていると思います。なぜなら、以下のステップ10に「このようなnが2つある場合は、大きい方のnを選択する」と書かれているからです。

そして Grant Wagner が言ったように:Math.ceil(x)またはMath.floor(x)x.toFixed()の代わりに。

以下はすべて ECMAScript言語仕様 からのものです。

15.7.4.5 Number.prototype.toFixed (fractionDigits)

小数点の後にfractionDigits桁の固定小数点表記で表される数値を含む文字列を返します。 fractionDigitsが未定義の場合、_0_が想定されます。具体的には、次の手順を実行します。

  1. fToInteger(fractionDigits)とする。 (fractionDigitsが未定義の場合、このステップは値_0_を生成します)。
  2. _f < 0_または_f > 20_の場合、RangeError例外をスローします。
  3. xをこの数値にします。
  4. xNaNの場合、文字列_"NaN"_を返します。
  5. sを空の文字列にします。
  6. _x ≥ 0_の場合、手順9に進みます。
  7. Sを_"-"_としましょう。
  8. _x = –x_としましょう。
  9. _x ≥ 10^21_の場合は、m = ToString(x)にしてステップ20に進みます。
  10. nを、_n ÷ 10^f – x_の正確な数学的値が可能な限りゼロに近い整数とする。そのようなnが2つある場合は、大きいnを選択します。
  11. _n = 0_の場合、mを文字列_"0"_にします。それ以外の場合は、mnの10進表現の数字で構成される文字列にします(先頭にゼロを付けずに)。
  12. _f = 0_の場合、ステップ20に進みます。
  13. kmの文字数とします。
  14. _k > f_の場合、手順18に進みます。
  15. zを、文字_f+1–k_の_'0'_オカレンスで構成される文字列とします。
  16. mを文字列zmの連結にします。
  17. _k = f + 1_としましょう。
  18. amの最初の_k–f_文字とし、bfの残りのm文字とします。
  19. mを3つの文字列a、_"."_、およびbの連結とします。
  20. 文字列smの連結を返します。

lengthメソッドのtoFixedプロパティは_1_です。

toFixedメソッドが複数の引数で呼び出された場合、動作は未定義です(セクション15を参照)。

実装では、toFixedの値に対してfractionDigitsの動作を_0_未満または_20_より大きい値に拡張できます。この場合、toFixedはそのような値に対してRangeErrorを必ずしもスローしません。

[〜#〜] note [〜#〜]toFixedの出力は、一部の値に対してtoStringよりも正確な場合がありますtoStringは、数値を隣接する数値と区別するのに十分な有効数字のみを出力するためです。たとえば、_(1000000000000000128).toString()_は_"1000000000000000100"_を返し、_(1000000000000000128).toFixed(0)_は_"1000000000000000128"_を返します。

12
some

2つの元の問題/質問に対処するには:

Math.round(num)vs num.toFixed(0)

ここでの問題は、これらは常に同じ結果をもたらすべきであるという誤解にあります。実際、それらは異なるルールによって管理されています。たとえば、負の数を見てください。 _Math.round_"round half up" をルールとして使用するため、Math.round(-1.5)が_-1_に評価されることがわかりますMath.round(1.5)は_2_と評価されます。

一方、_Number.prototype.toFixed_は、基本的に "ゼロから離れた半分" と同等のものを、 仕様のステップ6 に従って使用します。これは本質的に、負の値を正の数として扱い、最後に負の符号を追加し直すことを意味します。したがって、_(-1.5).toFixed(0) === "-2"_と_(1.5).toFixed(0) === "2"_は、すべての仕様に準拠したブラウザーで真のステートメントです。これらの値は数値ではなく文字列であることに注意してください。さらに、-1.5.toFixed(0)-(1.5).toFixed(0)は、演算子の優先順位により_=== -2_(数字)であることに注意してください。

ブラウザの不整合

最新のブラウザ—または、少なくともこの記事の執筆時点でサポートが期待されるもの IEを除く—すべての仕様を正しく実装する必要があります。 ( Reneeのコメント によれば、Operaで指摘したtoFixedの問題は、おそらくChromeと同じJSエンジンを使用し始めてから修正されました。 。)すべてのブラウザで仕様が一貫して実装されていたとしても、仕様で定義された動作、特にtoFixed丸めについては、「単なる致命的な」JS開発者にとっては少し直感的ではない可能性があります。真の数学的精度を期待してください。例としてV8 JSエンジンに提出された Javascript toFixed Not Rounding および この「意図したとおりに動作する」バグ を参照してください。

結論

要するに、これらは、2つの異なる戻り値型と丸めのための2つの異なるルールセットを持つ2つの異なる関数です。

他の人が示唆しているように、「特定のユースケースに合った関数を使用してください」と言いたいと思います(toFixedの特殊性、特にIEの誤った実装に注意してください)。 個人的には、他の人が述べたように、_Math.round/ceil/floor_の明示的な組み合わせを推奨することにもっと傾倒するでしょう。 編集:...ただし、戻って説明を読んだ後、ユースケース(整数に丸める)は、適切な名前の_Math.round_関数を確実に呼び出します。

10
Noyo

toFixed()は文字列値を返します。 JavaScriptから:決定版ガイド

数値を、小数点以下の指定された桁数を含む文字列に変換します。

Math.round()は整数を返します。

明らかに、toFixed()はお金をより多く使用するようです。たとえば、

'$' + 12.34253.toFixed(2)= '$ 12.34'

toFixed()が適切に丸められていないように見えることは非常に残念です!

6
Matt

toFixed(0)の代わりに、必要に応じてMath.ceil()またはMath.floor()を使用します。

2
Grant Wagner

一貫性のない回答が得られている場合は、間違いなくそのように見えます。

Usin toFixed(0)を使用する目的は、10進数を整数に変換することだけであると推測できます。その時点でMath.floor()をお勧めします。 この質問 でこれを行う最善の方法についてもう少し議論があります。

0
Daniel Lew