fix(backend): float != must be UNORDERED so nan != nan is true [F0.9]
emitCmpNe lowered float `!=` to `LLVMRealONE` (ordered not-equal), which is false when either operand is NaN. That made `nan != nan` false in native code — breaking the canonical `x != x` NaN test, making `!=` non-complementary with `==` for NaN, and disagreeing with the interpreter. Change the float predicate to `LLVMRealUNE` (unordered not-equal): true if either operand is NaN OR they are unequal. For all non-NaN operands `UNE` ≡ `ONE`, so only NaN-involving comparisons change (toward correct). The integer predicate (`LLVMIntNE`) and `emitCmpEq` (`OEQ`) are unchanged, so `nan == nan` stays false and `!=` is now the exact complement of `==`. - Regression: examples/0150-types-float-ne-unordered-nan.sx (fails before, passes after; also pins #run/comptime == runtime agreement). - specs.md: documents float comparison / NaN semantics (Operators). - Resolves issue 0091 (issues/0091-float-ne-ordered-nan.md).
This commit is contained in:
35
examples/0150-types-float-ne-unordered-nan.sx
Normal file
35
examples/0150-types-float-ne-unordered-nan.sx
Normal file
@@ -0,0 +1,35 @@
|
||||
// Float `!=` is UNORDERED not-equal: `nan != nan` is true (the canonical
|
||||
// `x != x` NaN idiom), and `!=` is the exact complement of `==` for every
|
||||
// float input — including NaN, where `nan == nan` is false (ordered `==`).
|
||||
// For all non-NaN operands unordered `!=` matches ordered `!=`, so finite
|
||||
// comparisons are unchanged. The native backend agrees with the interpreter.
|
||||
//
|
||||
// Regression (issue 0091): the LLVM backend lowered float `!=` to ordered
|
||||
// not-equal (LLVMRealONE), so `nan != nan` was false in native code.
|
||||
#import "modules/std.sx";
|
||||
|
||||
main :: () {
|
||||
// Produce a genuine NaN without any numeric-limit accessor: 0.0 / 0.0.
|
||||
z := 0.0;
|
||||
nan := z / z;
|
||||
|
||||
// The fix: `!=` is unordered, `==` is ordered.
|
||||
print("nan != nan: {}\n", nan != nan); // true
|
||||
print("nan == nan: {}\n", nan == nan); // false
|
||||
print("nan != 1.0: {}\n", nan != 1.0); // true
|
||||
print("nan == 1.0: {}\n", nan == 1.0); // false
|
||||
|
||||
// Complementarity holds for finite operands too (unchanged behavior).
|
||||
print("1.0 != 2.0: {}\n", 1.0 != 2.0); // true
|
||||
print("1.0 != 1.0: {}\n", 1.0 != 1.0); // false
|
||||
print("2.0 != 2.0: {}\n", 2.0 != 2.0); // false
|
||||
|
||||
// Native codegen converges with the comptime interpreter.
|
||||
print("comptime nan != nan: {}\n", #run nan_ne_nan());
|
||||
}
|
||||
|
||||
nan_ne_nan :: () -> bool {
|
||||
z := 0.0;
|
||||
n := z / z;
|
||||
return n != n;
|
||||
}
|
||||
1
examples/expected/0150-types-float-ne-unordered-nan.exit
Normal file
1
examples/expected/0150-types-float-ne-unordered-nan.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
nan != nan: true
|
||||
nan == nan: false
|
||||
nan != 1.0: true
|
||||
nan == 1.0: false
|
||||
1.0 != 2.0: true
|
||||
1.0 != 1.0: false
|
||||
2.0 != 2.0: false
|
||||
comptime nan != nan: true
|
||||
Reference in New Issue
Block a user