!= 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.
|
=(- 0.0 f64-inf)=: the decision wrote =(- f64-inf)=, and there is no unary minus.
|
||||||
Rules out Clojure's =##Inf= reader literal.
|
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
|
** DONE A u64 constant above 2^63 cannot be written in decimal
|
||||||
CLOSED: [2026-09-25]
|
CLOSED: [2026-09-25]
|
||||||
An integer written at or above 2^63 — a decimal up to 2^64 - 1, or hex with the
|
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.");
|
ordered.");
|
||||||
("!=", "!= [equal? ...] bool",
|
("!=", "!= [equal? ...] bool",
|
||||||
"All different: (!= a b c) is true when every operand differs from every \
|
"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",
|
("<", "< [ordered? ...] bool",
|
||||||
"Less than, chained: (< a b c) is a < b and b < c, and every operand is \
|
"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 \
|
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"
|
| Tast.Ge -> if signed then "sge" else "uge"
|
||||||
| _ -> assert false
|
| _ -> 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
|
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"
|
| Tast.Le -> "ole" | Tast.Gt -> "ogt" | Tast.Ge -> "oge"
|
||||||
| _ -> assert false
|
| _ -> 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.
|
(* Parity, which on [ucomis] means "unordered": one of the operands was a NaN.
|
||||||
Nothing else in this file reads it. *)
|
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
|
(* [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
|
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.
|
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
|
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,
|
comparison Flan has but one is LLVM's *ordered* one ([emit.ml]'s
|
||||||
one, olt, ...), which answers false for a NaN, and [setb] after an
|
[fcmp_op]: oeq, olt, ...), which answers false for a NaN, and [setb] after
|
||||||
unordered compare answers true. So [<] and [<=] swap their operands and ask
|
an unordered compare answers true. So [<] and [<=] swap their operands and
|
||||||
for a/ae, which are the two codes a NaN makes false; [=] and [!=] cannot be
|
ask for a/ae, which are the two codes a NaN makes false; [=] cannot be
|
||||||
spelled by one code at all and take a second [setnp] beside them.
|
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
|
[(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
|
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"
|
| _ -> unsupported "not a comparison"
|
||||||
|
|
||||||
let float_ordered (p : Tast.prim) =
|
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) =
|
let is_cmp (p : Tast.prim) =
|
||||||
match p with
|
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;
|
movzx8 f.b ~dst:rcx ~src:rcx;
|
||||||
and_rr f.b ~dst:rax ~src:rcx
|
and_rr f.b ~dst:rax ~src:rcx
|
||||||
end
|
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
|
end else begin
|
||||||
load_loc f ~reg:rax la a.Tast.ty;
|
load_loc f ~reg:rax la a.Tast.ty;
|
||||||
load_loc f ~reg:rcx lb b.Tast.ty;
|
load_loc f ~reg:rcx lb b.Tast.ty;
|
||||||
|
|||||||
@ -60,6 +60,10 @@
|
|||||||
(print " ")
|
(print " ")
|
||||||
(println (if ok "ok" "WRONG")))
|
(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
|
(defn main [] i32
|
||||||
;; The integers, each printed as the exact decimal the expected output pins.
|
;; The integers, each printed as the exact decimal the expected output pins.
|
||||||
(println i8-max)
|
(println i8-max)
|
||||||
@ -128,4 +132,13 @@
|
|||||||
(say "f32-inf negated" (< (- (f32 0.0) f32-inf) (- (f32 0.0) f32-max)))
|
(say "f32-inf negated" (< (- (f32 0.0) f32-inf) (- (f32 0.0) f32-max)))
|
||||||
(say "f64-nan" (not (= f64-nan f64-nan)))
|
(say "f64-nan" (not (= f64-nan f64-nan)))
|
||||||
(say "f32-nan" (not (= f32-nan f32-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)
|
0)
|
||||||
|
|||||||
@ -5440,7 +5440,9 @@ level "1"
|
|||||||
f32's least value negates its greatest ok\n\
|
f32's least value negates its greatest ok\n\
|
||||||
f64'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-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
|
in
|
||||||
outputs "type limits" "programs/limits.flan" limits_out;
|
outputs "type limits" "programs/limits.flan" limits_out;
|
||||||
outputs ~opt:"-O0" "type limits, -O0" "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