次の場合、引き算の浮動小数点誤差によりゼロ除算は可能でしょうか?
float x, y, z;
...
if (y != 1.0)
z = x / (y - 1.0);
言い換えれば、以下はより安全ですか?
float divisor = y - 1.0;
if (divisor != 0.0)
z = x / divisor;
次の場合、引き算の浮動小数点誤差によりゼロ除算は可能でしょうか?
float x, y, z;
...
if (y != 1.0)
z = x / (y - 1.0);
言い換えれば、以下はより安全ですか?
float divisor = y - 1.0;
if (divisor != 0.0)
z = x / divisor;
IEEE-754 浮動小数点を想定すると、それらは同等です。
有限の x と y について、x == y の場合に限り x - y == 0 であるというのは、漸進的なアンダーフローを想定した FP 算術の基本定理です。
非正規の結果が (段階的なアンダーフローではなく) ゼロにフラッシュされる場合、この定理は結果 x - y が正規の場合にのみ成立します。1.0 は適切にスケーリングされているためy - 1.0
、非正規化されることはy - 1.0
なく、アンダーフローの処理方法に関係なく、y が正確に 1.0 である場合にのみゼロになります。
もちろん、C++ は IEEE-754 を保証しませんが、この定理はほとんどの「妥当な」浮動小数点システムに当てはまります。
これにより、正確にゼロで割ることができなくなりますが、それでも結果として終わらないというわけではありません+/-inf
。分母はまだ十分に小さいため、答えは で表現できず、double
最終的には になりますinf
。例えば:
#include <iostream>
#include <limits>
int main(int argc, char const *argv[])
{
double small = std::numeric_limits<double>::epsilon();
double large = std::numeric_limits<double>::max() / small;
std::cout << "small: " << small << std::endl;
std::cout << "large: " << large << std::endl;
return 0;
}
このプログラムsmall
では はゼロではありませんが、とlarge
の範囲を超えるほど小さいdouble
ですinf
。
2つのコードスニペット()の間に違いはありません。実際、divisor
変数をこれ以上使用しないと仮定すると、オプティマイザーは両方のフラグメントを同じバイナリコードに最適化することもできます。
ただし、浮動小数点ゼロで除算0.0
しても実行時エラーは発生せず、代わりにinf
またはが生成されることに注意してください。-inf