浮動小数点数の比較についてまとめます。
浮動小数点数同士の(大小)関係
普通の実数 $x$ と $y$ の間には、
- $x$ が $y$ より小さい (less than): $x<y$
- 等しい (equal): $x=y$
- $x$ が $y$ より大きい (greater than): $x>y$
のうち、ちょうど1つの関係が成り立ちます。
浮動小数点数の場合、この3つの他に
- 順序づけられない (unordered): 少なくとも一方がNaN
という関係があり得ます。
普通の、数直線にプロットできる浮動小数点数は問題ないでしょう。ここでは特殊な浮動小数点数について確認します。
まずはゼロの符号です。IEEE 754の浮動小数点数のゼロは符号を持ち、+0と-0の2通りがあり得ます。しかし、+0と-0は比較の際には等しいものとして取り扱われます。0とそれ以外の値の比較は、数学的な0の取り扱いに準じます。
次に、無限大です。正の無限大と負の無限大ですが、正の無限大は数直線上のどんな数よりも大きいものとして取り扱われ、負の無限大は数直線上のどんな数よりも小さいものとして取り扱われます。また、正の無限大同士、負の無限大同士は等しいものとして取り扱われます。
最後に、NaNです。NaNはどんな数とも順序づけられないものとして扱われます。NaNはNaN自身とも順序づけられません。
浮動小数点数の比較演算
浮動小数点数に対する =
や <
などの比較演算は、オペランド同士の関係を調べて、比較の種類ごとにtrueを返すかfalseを返すか決めます。基本的な比較演算については次のようになります:
-
=
: 関係がequalの時にtrue、それ以外でfalse -
>
: 関係がgreater thanの時にtrue、それ以外でfalse -
>=
($\ge$): 関係がequalまたはgreater thanの時にtrue、それ以外でfalse -
<
: 関係がless thanの時にtrue、それ以外でfalse -
<=
($\le$): 関係がless thanまたはequalの時にtrue、それ以外でfalse
これだけ見ると至極真っ当で直感的ですが、NaNが絡むと厄介になってきます。以下の2点を挙げておきます。
- NaN同士の関係はequalではなくunorderedなので、比較演算
NaN = NaN
はfalseを返します。 - NaNが絡む関係はunorderedなので、
x < y
の否定!(x < y)
はx >= y
と一致しません。
浮動小数点例外
浮動小数点演算は、異常な状態に遭遇した時に例外を発生させることがあります。例外を発生と言っても大域脱出を起こすとは限らず、大抵は該当するフラグを立てて終わることも多いですが。
浮動小数点数の比較演算はinvalid operation例外を発生させる場合があります。invalid operation例外が発生する可能性があるのは、オペランドにNaNが含まれている場合です。
まず、オペランドにsignaling NaNが含まれている場合は必ずinvalid operation例外が発生します。x = x
のような一見何もしないような比較演算であってもです。
次に、オペランドにsignaling NaNが含まれていないがquiet NaNが含まれている場合です。この場合に対して、IEEE 754では例外を発生させる版と発生させない版の両方を定義しています。名前にQuietが含まれるものはquiet NaNに対して例外を発生させませんが、名前にSignalingが含まれるものはquiet NaNに対しても例外を発生させます。
述語の名前 | 比較結果 | quiet NaNに対して例外を | プログラミング言語での |
---|---|---|---|
compareQuietEqual |
= と同じ |
発生させない | = |
compareQuietGreater |
> と同じ |
発生させない | |
compareQuietGreaterEqual |
>= と同じ |
発生させない | |
compareQuietLess |
< と同じ |
発生させない | |
compareQuietLessEqual |
<= と同じ |
発生させない | |
compareSignalingEqual |
= と同じ |
発生させる | |
compareSignalingGreater |
> と同じ |
発生させる | > |
compareSignalingGreaterEqual |
>= と同じ |
発生させる | >= |
compareSignalingLess |
< と同じ |
発生させる | < |
compareSignalingLessEqual |
<= と同じ |
発生させる | <= |
これらの論理否定も定義されています:
述語の名前 | 比較結果 | quiet NaNに対して例外を | プログラミング言語での |
---|---|---|---|
compareQuietNotEqual |
= の否定 |
発生させない | != |
compareQuietNotGreater |
> の否定 |
発生させない | |
compareQuietLessUnordered |
>= の否定(関係がlessまたはunorderedの場合にtrue) |
発生させない | |
compareQuietNotLess |
< の否定 |
発生させない | |
compareQuietGreaterUnordered |
<= の否定(関係がgreaterまたはunorderedの場合にtrue) |
発生させない | |
compareSignalingNotEqual |
= の否定 |
発生させる | |
compareSignalingNotGreater |
> の否定 |
発生させる | !(_ > _) |
compareSignalingLessUnordered |
>= の否定(関係がlessまたはunorderedの場合にtrue) |
発生させる | !(_ >= _) |
compareSignalingNotLess |
< の否定 |
発生させる | !(_ < _) |
compareSignalingGreaterUnordered |
<= の否定(関係がgreaterまたはunorderedの場合にtrue) |
発生させる | !(_ <= _) |
また、関係がunorderedであることを判定する述語も定義されています。
述語の名前 | 比較結果 | quiet NaNに対して例外を |
---|---|---|
compareQuietUnordered | 関係がunordered | 発生させない |
compareQuietOrdered | 関係がequal, less, greaterのいずれか | 発生させない |
IEEE 754では、プログラミング言語組み込みの比較演算 =
, !=
, >
, >=
, <
, <=
に対応させる述語を推奨という形で規定しています。上記の表の「プログラミング言語での…」の欄がそれです。
C言語の場合
C言語の場合、組み込みの比較演算子はIEEE 754準拠です(Annex F.4)。それを補完するように <math.h>
でマクロが定義されています:
#include <math.h>
int isgreater(<real-floating> x, <real-floating> y); // compareQuietGreater
int isgreaterequal(<real-floating> x, <real-floating> y); // compareQuietGreaterEqual
int isless(<real-floating> x, <real-floating> y); // compareQuietLess
int islessequal(<real-floating> x, <real-floating> y); // compareQuietLessEqual
int islessgreater(<real-floating> x, <real-floating> y);
int isunordered(<real-floating> x, <real-floating> y); // compareQuietUnordered
これらはマクロで、引数は実数の浮動小数点数型である必要があります。
islessgreater
はIEEE 754-1985に存在したLG (less or greater)述語のQuiet版で、関係がless thanまたはgreater thanの場合にtrueを返し、引数にquiet NaNが含まれる場合にinvalid例外を発生させません。
C11の時点ではcompareSignalingEqualに相当する演算子・マクロはありません。C23ではcompareSignalingEqualに相当するマクロが
#include <math.h>
int iseqsig(<real-floating> x, <real-floating> y); // compareSignalingEqual
として追加される見込みです(N2731に記載あり)。
その他の比較方法
IEEE 754では、通常の比較演算の他にtotalOrderという比較方法も規定しています。これは
- 0の符号を区別する
- 同じ数のcohortを区別する
- NaNの符号を区別する
- signaling NaNに対してもquiet NaNに対しても例外を発生させない
などの特徴があります。