web-dev-qa-db-ja.com

多くの(古い)プログラムがround(input)ではなくfloor(0.5 + input)を使用するのはなぜですか?

このコード のように、タイブレークの周りの入力を与える戻り値に違いがあります。

int main()
{
    std::cout.precision(100);

    double input = std::nextafter(0.05, 0.0) / 0.1;
    double x1 = floor(0.5 + input);
    double x2 = round(input);

    std::cout << x1 << std::endl;
    std::cout << x2 << std::endl;
}

どの出力:

1
0

しかし、それらは最終的には異なる結果に過ぎず、好みの結果を選択します。 floor(0.5 + input)の代わりにround(input)を使用する「古い」C/C++プログラムがたくさんあります。

歴史的な理由はありますか? CPUで一番安いですか?

79
markzzz

std::round はC++ 11で導入されました。それ以前は、 std::floor のみが利用可能であったため、プログラマーはそれを使用していました。

114
haccks

歴史的な理由はまったくありません。この種の逸脱は、一年のドット以来ありました。彼らは、非常にいたずらを感じているときにこれを行います。これは浮動小数点演算の深刻な乱用であり、多くの経験豊富なプロのプログラマーがそれに陥ります。 Java bodsでさえバージョン1.7まででした。おかしいです。

私の推測では、まともな独創的なドイツの丸め関数は、C++ 11まで正式に利用できなかったが(CはC99でそれらを取得しているにもかかわらず)、いわゆる代替を採用する理由は本当にありません。

次のとおりです:floor(0.5 + input)は、対応するstd::round呼び出しと同じ結果を常に回復するとは限りません!

理由は非常に微妙です。ドイツの丸めのカットオフポイント、整数aa.5は、宇宙の偶然の性質により、ダイアディック有理数です。これはIEEE754の浮動小数点で正確に2の52乗まで表現できるため、その後は丸めは何の操作も行われないため、std::roundは常に適切に機能します。他の浮動小数点スキームについては、ドキュメントを参照してください。

しかし、0.5doubleに追加すると、不正確さが生じ、一部の値がわずかにアンダーシュートまたはオーバーシュートする可能性があります。考えてみると、2つのdouble値を一緒に追加します-それは無意識のdenary変換の始まりです-そして、入力の非常に強い関数(丸め関数など)である関数を適用すると、涙が止まりません。

やらないでください

リファレンス: Math.round(0.49999999999999994)が1を返す理由

18
Bathsheba

私はこれがあなたが間違っているところだと思う:

しかし、それらは最終的には異なる結果に過ぎず、好みの結果を選択します。 round(input)の代わりにfloor(0.5 + input)を使用する「古い」C/C++プログラムがたくさんあります。

そうではありません。 ドメインの正しい丸めスキームを選択する必要があります。金融アプリケーションでは、銀行員のルールを使用して丸めます(ちなみにフロートは使用しません)。ただし、サンプリング時にstatic_cast<int>(floor(f + .5))を使用して切り上げるとサンプリングノイズが少なくなり、ダイナミックレンジが増加します。ピクセルを揃えるとき、つまり位置をスクリーン座標に変換するとき、他の丸め方法を使用すると、穴、ギャップ、その他のアーティファクトが生じます。

9
Michaël Roy

単純な理由としては、数値を丸める方法が異なるため、使用する方法を知らない限り、結果が異なる可能性があります。

Floor()を使用すると、結果と一貫性を保つことができます。フロートが.5以上の場合、フロートを追加すると次のintにバンプアップします。ただし、.49999は小数を切り捨てます。

4
MivaScott

多くのプログラマーは、他の言語でプログラミングするときに学んだイディオムを採用しています。すべての言語にround()関数があるわけではなく、これらの言語ではfloor(x + 0.5)を代わりに使用するのが普通です。これらのプログラマーがC++の使用を開始したとき、組み込みのround()があることを常に認識しているわけではなく、従来のスタイルを使用し続けています。

言い換えれば、何かをするコードがたくさんあるからといって、それをする正当な理由があるわけではありません。すべてのプログラミング言語でこの例を見つけることができます。覚えておいてください スタージョンの法則

すべての90パーセントはがらくたです

2
Barmar