ceil(),round(),floor()関数の実行速度 - ページ 4

 
Alexey Navoykov:

つまり、DBL_EPSILONは小数点以下16桁:2.2204460492503131e-016と なります。

そして、あなたの場合は、その差が1e-16と、εの2倍しかないので、実際に得をしているのです。


0.9999999997では動作しますが、0.999999999998では動作しなくなりました。

 
Nikolai Semko:

0.999999999997から動作するが、0.999999999998からは動作しなくなった。


ここにも仕掛けがあります。

Y=(int)(x+1-2*DBL_EPSILON);
Print(Y);                             // 3
Y=(int)(x+1-DBL_EPSILON-DBL_EPSILON);
Print(Y);                             // 4
 

10進数表記で2.2204460492502503131e-016という数字は、フォーマットダブル (https://msdn.microsoft.com/ru-ru/library/6bs3y5ya(v=vs.100).aspx )の解像度を意味 し、これ

x + 1.0が1.0にならないような最小の正の数x。

- は、1.0を加えると数値が変化するような最小の正の数である。つまり、ある数値の表現の相対的な誤差である。つまり、16の場合はこの境界線に10進数の9がこれだけ含まれ、最後の64の場合は1の4倍、8の場合は半分になるのです。そこで、できるだけ高い精度で丸めを高速化するために、この精度を計算することになる。これらの計算が、通常の丸め関数の性能より速くなることはまずないでしょう。MQLは数値のアドレスを直接扱うことができないので、数値の順序のビットを取ることができず、基底2でのlog関数のアナログを考案する必要があるのです。速効性は期待できない。

Пределы констант с плавающей запятой
Пределы констант с плавающей запятой
  • msdn.microsoft.com
В следующей таблице представлены ограничения на значения констант с плавающей запятой. Эти ограничения также заданы в стандартном файле заголовка FLOAT.H. Ограничения на константы с плавающей запятой КонстантаЗначениеЗначение Количество цифр q, при котором число с плавающей запятой с q десятичными цифрами можно округлить в представление с...
 
Nikolai Semko:

ここにも仕掛けがあります。

数学の演算では、プラスかマイナスのどちらかの誤差が発生することがわかった。したがって、一般的にはウラジミールが指摘したように、結果を正規化することが必要です。

 

ceil(), floor() は、FPUの丸めモードがμlから制御できないため、再現性が低い。おそらく多かれ少なかれ適切に動作するのは round() だけで、これは標準によると正確ではありません。ちなみに、C型round()は大体こんな感じです。

double round(double x)
{
    fenv_t save_env;
    feholdexcept(&save_env);
    double result = rint(x);
    if (fetestexcept(FE_INEXACT)) {
        fesetround(FE_TOWARDZERO);
        result = rint(copysign(0.5 + fabs(x), x));
    }
    feupdateenv(&save_env);
    return result;
}
 
ここで丸めたいことを理解するには、DOUBLE形式をINT形式に変換するときに、何がどのように失われるかを正確に知る必要があります。この場合、丸めた後に残るのは整数部分のみです。結果として、任意の小数点 以下の桁数の数値は削除されて整数部分のみが残ることになります。
の例です。
2.1758716871 -> (int)2.1758716871 = 2
2.0000000001 -> (int)2.0000000001 = 2
2.9999999999 -> (int)2.9999999999 = 2
これは、数値が再フォーマットされたときの精度の損失 と呼ばれるものです。
仍って
MathFloor(x)  =  (int)(x)
MathCeil(x)   =  (int)(x)+1;
MathRound(x)  =  (int)(x+0.5)

敬具
 
Andrey Kisselyov:
ここで丸めたいことを理解するためには、DOUBLE形式をINT形式に変換するときに、何がどのように失われるかを正確に知る必要があります。この場合、切り捨てた後に残る整数部分だけが得られます。結果として、任意の小数点 以下の桁数、カンマ以降の桁数が削除されて、整数部分だけが残ります。
example:これは、数値が再フォーマットされたときの精度の損失 と呼ばれるものです。
仍って

敬具

なぜ、こんなに複雑なのか?

MathRound(x)  - >  (int)(x)+(int)(2.*(x-(int)(x)));

よりシンプルなものと比べて、どのように優れているのでしょうか?

MathRound(x)  ->  (int)(x+0.5);
 
Alexey Navoykov:

数学の演算では、プラスかマイナスのどちらかの誤差が発生することがわかった。したがって、ウラジミール氏が指摘するように、一般的なケースでは、結果を正規化する必要があるのです。


pavlick_:

ceil(), floor() は、FPUの丸めモードがμlから制御できないため、再現性が低い。おそらく多かれ少なかれ適切に動作するのは round() だけで、規格どおり厳密なものではありません。ちなみに、C型round()はおおよそこんな感じです。


もちろん、その通りです。でも、繰り返します。99.9%のタスクでは、丸め関数を、小数型の(int)や(long)への変換を用いたより高速な代替関数に置き換えることが絶対に正しいのです。経験豊富なプログラマーは、このことを意識して、必要なときに応用すればよいのです。

Для положительных x:
y=floor(x); -> y=(int)x;
y=ceil(x);  -> y=(int)(x+0.9999999999999997);
y=round(x); -> y=(int)(x+0.5);
 
Nikolai Semko:

なぜ、こんなに複雑なのか?

よりシンプルなものと比べて、どのように優れているのでしょうか?

そのように試してみてください。

ニコライ・セムコ
経験豊富なプログラマーは、この事実を認識し、適切なタイミングで適用すればよいのです。

そうしなければならないのではなく、それを知っていなければならないのです。

敬意を込めて。

追伸:あなたの計算式は、値の集合全体に対して正しいものではありません。
y=ceil(x);  -> y=(int)(x+0.9999999999999997);
は、私のものを見て、私は短い式にラウンドを更新し、今リストは完全で、完全に機能している、それを使用してください。
 
Andrey Kisselyov:


追伸:あなたの計算式は、値の集合全体に対して正しいわけではありません。

は、私のものを見て、私は短い式にラウンドを更新し、今リストは完全で、完全に機能している、それを使用してください。

みぬく

x = 3 (任意の整数)のとき、ジャムが発生する :))