!= over floats is unordered, so a NaN is unequal to itself on both backends as it already was on the dyn side
This commit is contained in:
parent
0b9ae9318e
commit
14bac84f53
6
TODO.org
6
TODO.org
@ -146,12 +146,6 @@ missed, so a program's own binding of one wins. Negative infinity is
|
||||
=(- 0.0 f64-inf)=: the decision wrote =(- f64-inf)=, and there is no unary minus.
|
||||
Rules out Clojure's =##Inf= reader literal.
|
||||
|
||||
** TODO (!= x x) is false for a NaN
|
||||
=!== on floats is LLVM's ordered =one= on both backends (=lib/emit.ml= =fcmp_op=,
|
||||
=lib/x86.ml= =float_cc=), so =(!= f64-nan f64-nan)= is =false= where C, Odin and
|
||||
IEEE 754 say =true=; =(not (= x x))= is the only NaN test that works. Changing it
|
||||
to =une= is a decision about what =!== means.
|
||||
|
||||
** DONE A u64 constant above 2^63 cannot be written in decimal
|
||||
CLOSED: [2026-09-25]
|
||||
An integer written at or above 2^63 — a decimal up to 2^64 - 1, or hex with the
|
||||
|
||||
@ -10064,7 +10064,8 @@ let builtins : (string * string * string) list =
|
||||
ordered.");
|
||||
("!=", "!= [equal? ...] bool",
|
||||
"All different: (!= a b c) is true when every operand differs from every \
|
||||
other, so (!= 1 2 1) is false. Over everything = accepts.");
|
||||
other, so (!= 1 2 1) is false. Over everything = accepts. A float NaN \
|
||||
is != to everything, itself included.");
|
||||
("<", "< [ordered? ...] bool",
|
||||
"Less than, chained: (< a b c) is a < b and b < c, and every operand is \
|
||||
evaluated once. Machine numbers and enums only — ordering a handle \
|
||||
|
||||
@ -1966,8 +1966,11 @@ let icmp_op signed = function
|
||||
| Tast.Ge -> if signed then "sge" else "uge"
|
||||
| _ -> assert false
|
||||
|
||||
(* [!=] is unordered and the rest are ordered, so a NaN is unequal to
|
||||
everything, itself included, and neither less, greater nor equal: IEEE 754's
|
||||
answers, and C's and Odin's. *)
|
||||
let fcmp_op = function
|
||||
| Tast.Eq -> "oeq" | Tast.Ne -> "one" | Tast.Lt -> "olt"
|
||||
| Tast.Eq -> "oeq" | Tast.Ne -> "une" | Tast.Lt -> "olt"
|
||||
| Tast.Le -> "ole" | Tast.Gt -> "ogt" | Tast.Ge -> "oge"
|
||||
| _ -> assert false
|
||||
|
||||
|
||||
22
lib/x86.ml
22
lib/x86.ml
@ -1317,18 +1317,20 @@ let int_cc ~signed (p : Tast.prim) =
|
||||
|
||||
(* Parity, which on [ucomis] means "unordered": one of the operands was a NaN.
|
||||
Nothing else in this file reads it. *)
|
||||
let cc_np = 11
|
||||
let cc_p = 10 and cc_np = 11
|
||||
|
||||
(* [ucomis] sets the flags the *unsigned* codes read, whichever way the
|
||||
operands are signed, so a float comparison never uses l/g — and it sets
|
||||
CF, ZF and PF all at once when either operand is a NaN.
|
||||
|
||||
That last part is why this is not simply the unsigned table. Every
|
||||
comparison Flan has is LLVM's *ordered* one ([emit.ml]'s [fcmp_op]: oeq,
|
||||
one, olt, ...), which answers false for a NaN, and [setb] after an
|
||||
unordered compare answers true. So [<] and [<=] swap their operands and ask
|
||||
for a/ae, which are the two codes a NaN makes false; [=] and [!=] cannot be
|
||||
spelled by one code at all and take a second [setnp] beside them.
|
||||
comparison Flan has but one is LLVM's *ordered* one ([emit.ml]'s
|
||||
[fcmp_op]: oeq, olt, ...), which answers false for a NaN, and [setb] after
|
||||
an unordered compare answers true. So [<] and [<=] swap their operands and
|
||||
ask for a/ae, which are the two codes a NaN makes false; [=] cannot be
|
||||
spelled by one code at all and takes a second [setnp] beside it. The one is
|
||||
[!=], which is [une] — true for a NaN, as IEEE 754 and C have it — and is
|
||||
[setne] or'd with [setp].
|
||||
|
||||
[(not (= x x))] is how [format-f64] in the prelude detects a NaN, and it is
|
||||
the whole of the difference: with [sete] alone, [(/ 0.0 0.0)] formatted as
|
||||
@ -1344,7 +1346,7 @@ let float_cc (p : Tast.prim) =
|
||||
| _ -> unsupported "not a comparison"
|
||||
|
||||
let float_ordered (p : Tast.prim) =
|
||||
match p with Tast.Eq | Tast.Ne -> true | _ -> false
|
||||
match p with Tast.Eq -> true | _ -> false
|
||||
|
||||
let is_cmp (p : Tast.prim) =
|
||||
match p with
|
||||
@ -3179,6 +3181,12 @@ and prim f (e : Tast.expr) (p : Tast.prim) (args : Tast.expr list) dst =
|
||||
movzx8 f.b ~dst:rcx ~src:rcx;
|
||||
and_rr f.b ~dst:rax ~src:rcx
|
||||
end
|
||||
else if p = Tast.Ne then begin
|
||||
movzx8 f.b ~dst:rax ~src:rax;
|
||||
setcc f.b ~cc:cc_p ~dst:rcx;
|
||||
movzx8 f.b ~dst:rcx ~src:rcx;
|
||||
or_rr f.b ~dst:rax ~src:rcx
|
||||
end
|
||||
end else begin
|
||||
load_loc f ~reg:rax la a.Tast.ty;
|
||||
load_loc f ~reg:rcx lb b.Tast.ty;
|
||||
|
||||
@ -60,6 +60,10 @@
|
||||
(print " ")
|
||||
(println (if ok "ok" "WRONG")))
|
||||
|
||||
(defn ne-f64 [a f64 b f64] bool (!= a b))
|
||||
(defn ne-f32 [a f32 b f32] bool (!= a b))
|
||||
(defn ne-dyn [a dyn b dyn] bool (!= a b))
|
||||
|
||||
(defn main [] i32
|
||||
;; The integers, each printed as the exact decimal the expected output pins.
|
||||
(println i8-max)
|
||||
@ -128,4 +132,13 @@
|
||||
(say "f32-inf negated" (< (- (f32 0.0) f32-inf) (- (f32 0.0) f32-max)))
|
||||
(say "f64-nan" (not (= f64-nan f64-nan)))
|
||||
(say "f32-nan" (not (= f32-nan f32-nan)))
|
||||
;; != is the one unordered comparison: a NaN is unequal to everything,
|
||||
;; itself included, and the dyn side agrees. The operands arrive as
|
||||
;; parameters so that no constant folder answers in the backend's place.
|
||||
(say "f64-nan != itself" (ne-f64 f64-nan f64-nan))
|
||||
(say "f32-nan != itself" (ne-f32 f32-nan f32-nan))
|
||||
(say "!= over ordinary floats"
|
||||
(and (ne-f64 1.0 2.0) (not (ne-f64 1.5 1.5))
|
||||
(ne-f32 (f32 1.0) (f32 2.0)) (not (ne-f32 (f32 1.5) (f32 1.5)))))
|
||||
(say "a dyn NaN != itself" (ne-dyn f64-nan f64-nan))
|
||||
0)
|
||||
|
||||
@ -5440,7 +5440,9 @@ level "1"
|
||||
f32's least value negates its greatest ok\n\
|
||||
f64's least value negates its greatest ok\n\
|
||||
f64-inf ok\nf32-inf ok\nf64-inf negated ok\nf32-inf negated ok\n\
|
||||
f64-nan ok\nf32-nan ok\n"
|
||||
f64-nan ok\nf32-nan ok\n\
|
||||
f64-nan != itself ok\nf32-nan != itself ok\n\
|
||||
!= over ordinary floats ok\na dyn NaN != itself ok\n"
|
||||
in
|
||||
outputs "type limits" "programs/limits.flan" limits_out;
|
||||
outputs ~opt:"-O0" "type limits, -O0" "programs/limits.flan" limits_out;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user