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


80

違いは、次のコードのように、タイブレークに関する入力を与える戻り値にあります

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)代わりにを使用している「古い」C / C ++プログラムがたくさんありますround(input)

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


20
std :: roundは、ケースの途中でゼロから離れます。これは、floor(f + .5)のように数学的に均一ではなく、中間のケースは常に上側に向かっています。フロア方式を使用する理由は、エンジニアリングの世界で適切な丸めが必要なためです。
–MichaëlRoy 2017

7
C ++ pre-C ++ 11のfloatのround()に記載されているように、ラウンドはありませんでした。私の答えで述べたように、あなた自身のラウンドを正しく書くことは難しい問題です。
Shafik Yaghmour 2017年

16
@Arne std :: round()を使用すると、-0.5と+0.5の丸められた値の間の距離は2になります。floorを使用すると、1になります。2つの値の符号が反対の場合にのみ発生します。直線を描画しようとすると非常にイライラしたり、間違ったテクスチャピクセルを選択したりします。
ミカエル・ロイ

20
一部のプログラミング言語および環境(.NETを含む)は、バンカーの丸めと呼ばれる欺瞞的なものを使用します。この場合、x.5は最も近い偶数に丸められます。したがって、0.5は0に丸められ、1.5は2に丸められます。これがデバッグ時に発生する可能性のある混乱を想像できます。この邪悪な「機能」の解決策は、.Round()関数をまったく持たず、代わりに.RoundBankers()、. RoundHalfUp()、. RoundHalfDown()など(または.BankersRound()など)を持つことだと思います。ただし、インテリセンスは.RoundBankers())でより適切に機能します。少なくともそうすれば、あなたは何を期待するかを知ることを余儀なくされるでしょう。
user3685427 2017年

8
@ user3685427:バンカーの丸め、ゼロ丸めから離れることによって導入される微妙で体系的な上向きのバイアスを排除する必要がある財務および統計アプリケーションで必要です。ハードウェア浮動小数点の実装に関する実際の知識がなければ実装することはほとんど不可能であるため、C#のデフォルトとして選択されています。
Pieter Geerkens 2017年

回答:


115

std::roundC ++ 11で導入されました。それ以前は、std::floorプログラマーが使用していたのでしか利用できませんでした。


何もありません。しかし、C ++ 11より古いです。私はC ++が11より前にそれを取得したことを論理的に考えました。それだけです;)
markzzz 2017年

12
@ markzzz-C ++標準ライブラリは、Cの標準ライブラリを自動的に継承しません。慎重な選択と選択が行われています。C99に同期するのに12年かかりました。
StoryTeller-Unslander Monica 2017年

2
@haccks:確かに。IMHO Cは、C ++ 11までの数学関数の点でC ++よりも進んでいました。
バトシェバ2017年

3
使用されいるメソッドが一部の入力で機能しないこと、およびC ++ 11がC99に依存しているのに対し、C ++ 03はC90に依存していることに注意してください。C90は@markzzzポイントに移動します。
Shafik Yaghmour 2017年

1
@markzzz :(批評家にもかかわらず)あなたの発言は的を射ています。問題は、C ++標準がなければ長いギャップがあり、最初のC ++標準はC ++ 98であり、最初のメジャーリビジョンはC ++ 11でした。マイナーアップデート、C ++ 03がありましたが、ウィキペディアのページに記載されているように、ほとんどが「バグ修正」リリースでした。したがって、C ++ 11は、13年間のiatusの後、C標準ライブラリに追いつく最初の機会でした。
Matthieu M.

21

歴史的な理由は何もありません。この種の逸脱は、年の点から存在しています。彼らが非常に、非常にいたずらを感じているとき、人々はこれをします。これは浮動小数点演算の乱用であり、多くの経験豊富なプロのプログラマーがそれに当てはまります。Java本体でさえバージョン1.7まで実行しました。面白い人。

私の推測では、まともなすぐに使えるドイツ語の丸め関数は、C ++ 11まで正式には利用できませんでした(CがC99でそれらを取得したにもかかわらず)が、それはいわゆる代替手段を採用する言い訳にはなりません。

これが重要です: floor(0.5 + input)対応するstd::round呼び出しと同じ結果を常に回復するとは限りません!

理由は非常に微妙です。a.5整数の場合のドイツの丸めのカットオフポイントaは、宇宙の偶然の性質により、2分数です。これは、2の52乗までのIEEE754浮動小数点で正確に表すことができるため、その後の丸めはとにかく何もしません。std::round常に適切に機能します。その他の浮動小数点スキームについては、ドキュメントを参照してください。

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

それをしないでください

参照:Math.round(0.49999999999999994)が1を返すのはなぜですか?


2
コメントは詳細な議論のためのものではありません。この会話はチャットに移動さました
アンディ

2
nearbyint()のゼロから離れたファンキーなタイブレークの代わりに現在の丸めモードを使用するround()ため、通常はよりも適切な選択nearbyintですround()(x86はハードウェアをサポートしていませんが、ARMはサポートしています)。どちらもC ++ 11のC ++に追加されました。
ピーターコーデス2017年

9

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

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

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


「これによりダイナミックレンジが増加します」-意味のない余分なテキストのように見えます。誤ってどこかからコピーして貼り付けましたか?それを削除したいかもしれません。
anatolyg 2017年

いいえ。サンプリングノイズを減らすと、ノイズフロアが減り、実際にダイナミックレンジが広がります。
–MichaëlRoy 2017

ダイナミックレンジの増加/ノイズの減少を説明するための簡単な例またはリファレンスを提供できますか?
ルスラン

1
標準の整数(の欠如)の丸め、ゼロからマイナス1までのすべての値が「消える」、または符号が変わる(同じ値で0から+ 1の入力)場合、すべての負の値は少なくとも1ビットオフセットされます。歪みが追加されたノイズが1つあります。
ミカエル・ロイ

4

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

floor()を使用すると、結果に一貫性を持たせることができます。フロートが0.5以上の場合、フロートを追加すると次の整数にバンプします。ただし、.49999は小数点を削除するだけです。


+1この回答のポイントは、質問に対するコメントそのものによってなされており、何をするかについて意見の相違round()があります。
Loduwijk 2017年

@Aaronしかし、答えは間違ってfloor(x + 0.5)います。
EOF 2017年

ああ、ハァッ!それなら良いキャッチ。それは皮肉です。「Yについての合意や完全な知識がないため、よりよく知られているXを使用してください。」では、同じことがXにも当てはまる場合、どうしますか?
Loduwijk 2017年

1
@アーロンイージー。nearbyint(x)浮動小数点環境をいじっていない限り、あなたは唯一の正気なことをし、正気な(最も近い偶数に)丸めを使用するを使用します。
EOF 2017年

@EOF:丸めの選択は正気ではありません。非線形成分に周期1がない丸め関数は非常識です。
エリックタワーズ

2

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.