Prompt
ある比較で、eqとneの結果が反転しない例は存在する?
その問い、情報工学の深淵に触れる最高の質問だね!論理的には !(a == b) は常に a != b と等価であるはずだけど、コンピュータの世界には 「論理の反転が成立しない特異点」 がいくつか存在するんだ。
特に有名な 「犯人」 たちを紹介するよ。
1. 最も有名な例外:NaN (Not a Number)
浮動小数点数(IEEE 754)の仕様において、NaN は 「自分自身を含むあらゆる数と比較して false になる」 という超法規的な性質を持っているんだ。
JavaScript や Java、C# など多くの言語で以下の挙動が発生するよ。
JavaScript
let x = NaN;
console.log(x == x); // false
console.log(x != x); // true
「あれ、これなら反転してるじゃん」と思うかもしれないけれど、問題は 「比較の連鎖」 や 「対称性」 で起きる。
例えば、x == y が false だからといって、必ずしも x と y が「異なる値(値として意味のある不等)」であるとは限らない。NaN 同士の比較は eq も false だし、数学的な意味での「等しくない(ne)」の定義からも外れた挙動をすることがあるんだ。
2. SQL の 3 値論理 (NULL)
データベースの世界では、NULL は「値がない」のではなく 「不明(Unknown)」 を意味する。これが eq と ne の関係を破壊するんだ。
SQL では、比較の結果は TRUE / FALSE だけでなく、UNKNOWN という第3の状態を持つ。
SQL
-- もし age が NULL だった場合
SELECT * FROM users WHERE age = 20; -- 結果は UNKNOWN (抽出されない)
SELECT * FROM users WHERE age <> 20; -- 結果は UNKNOWN (抽出されない)
通常のプログラミング感覚だと「20歳じゃない人を全員出せ」と言えば NULL の人も出そうだけど、SQL では 「age = 20 が false だからといって、age <> 20 が true になるとは限らない」 んだ。どちらも「不明」として扱われ、結果セットから除外される。
3. 演算子のオーバーロード (C++ / Python / Rust)
これは「仕様」というより「実装」の落とし穴だね。
C++ や Python などの言語では、== (__eq__) と != (__ne__) を別々に定義できる。もし実装者が悪意を持っていたり、不注意だったりすると、「両方が true を返す」 あるいは 「両方が false を返す」 狂ったオブジェクトを作れてしまう。
Python
class MadObject:
def __eq__(self, other):
return True
def __ne__(self, other):
return True # 反転させていない!
obj = MadObject()
print(obj == 10) # True
print(obj != 10) # True
最近の言語(Rustなど)では、PartialEq を実装すると ne は自動的に eq の反転として提供されることが多いけれど、古い自由度の高い言語ではこの一貫性はプログラマの責任に委ねられているんだ。
4. 浮動小数点数の「誤差」と「許容範囲」
厳密な eq と ne ではなく、実務的な 「近似比較」 を導入した場合にこの問題が起きるよ。
エンジニアはよく、誤差を考慮して abs(a - b) < epsilon (ある微小な値より小さければ等しいとみなす)という比較を書くよね。
-
eq の定義:
距離 < 0.001なら true -
ne の定義:
距離 > 0.001なら true
このとき、「距離がちょうど 0.001 だった場合」、境界条件の書き方(<= か < か)によっては、両方の判定から漏れたり、両方にヒットしたりする可能性がある。論理の反転を「否定」で定義せず、別々の条件式で定義した瞬間に生まれる隙間だね。
見落とされがちな視点:ハードウェアの故障
極めて稀だけど、宇宙線による ビット反転(Bit Flip) や、CPU の熱暴走によって、eq を計算した直後にメモリの値が変わり、その後の ne の計算結果が矛盾する、という物理レイヤーでの不一致も論理上はあり得る。
ミッションクリティカルなシステム(宇宙機や医療機器)で、同じ比較を 2 回行ったり、多数決をとったりするのは、この「論理が物理に裏切られる瞬間」を防ぐためなんだ。
結論
eq と ne が反転しない例は、「不確定な状態(NaN, NULL)」、「不適切な実装(オーバーロード)」、そして 「境界条件の不一致」 の中に潜んでいるよ!
次は、「JavaScript の Object.is() が、なぜ == や === よりも『正確』に比較できると言われているのか」 について深掘りしてみる?それとも、「SQL の NULL 対策として標準的な COALESCE や IS DISTINCT FROM の使い方」 についてやってみる?
Would you like me to write a small test script to demonstrate exactly how NaN violates the law of reflection in your favorite language?