Theory IEEE_Single_NaN
section ‹Specification of the IEEE standard with a single NaN value›
theory IEEE_Single_NaN
imports
IEEE_Properties
begin
text ‹This theory defines a type of floating-point numbers that contains a single NaN value, much
like specification level~2 of IEEE-754 (which does not distinguish between a quiet and a
signaling NaN, nor between different bit representations of NaN).
In contrast, the type @{typ ‹('e, 'f) float›} defined in {\tt IEEE.thy} may contain several
distinct (bit) representations of NaN, much like specification level~4 of IEEE-754.
One aim of this theory is to define a floating-point type (along with arithmetic operations) whose
semantics agrees with the semantics of the SMT-LIB floating-point theory at
\url{https://smtlib.cs.uiowa.edu/theories-FloatingPoint.shtml}. The following development
therefore deviates from {\tt IEEE.thy} in some places to ensure alignment with the SMT-LIB
theory.›
text ‹Note that we are using HOL equality (rather than IEEE-754 floating-point equality) in the
following definition. This is because we do not want to identify~$+0$ and~$-0$.›
definition is_nan_equivalent :: "('e, 'f) float ⇒ ('e, 'f) float ⇒ bool"
where "is_nan_equivalent a b ≡ a = b ∨ (is_nan a ∧ is_nan b)"
quotient_type (overloaded) ('e, 'f) floatSingleNaN = "('e, 'f) float" / is_nan_equivalent
by (metis equivpI is_nan_equivalent_def reflpI sympI transpI)
text ‹Note that @{typ "('e, 'f) floatSingleNaN"} does not count the hidden bit in the significand.
For instance, IEEE-754's double-precision binary floating point format {\tt binary64} corresponds
to @{typ "(11,52) floatSingleNaN"}. The corresponding SMT-LIB sort is {\tt (\_ FloatingPoint 11 53)},
where the hidden bit is counted. Since the bit size is encoded as a type argument, and Isabelle/HOL
does not permit arithmetic on type expressions, it would be difficult to resolve this difference
without completely separating the definition of @{typ "('e, 'f) floatSingleNaN"} in this theory
from the definition of @{typ "('e, 'f) float"} in IEEE.thy.›
syntax "_floatSingleNaN" :: "type ⇒ type ⇒ type" (‹'(_, _') floatSingleNaN›)
syntax_types "_floatSingleNaN" ⇌ floatSingleNaN
text ‹Parse ‹('a, 'b) floatSingleNaN› as ‹('a::len, 'b::len) floatSingleNaN›.›
parse_translation ‹
let
fun float t u = Syntax.const @{type_syntax floatSingleNaN} $ t $ u;
fun len_tr u =
(case Term_Position.strip_positions u of
v as Free (x, _) =>
if Lexicon.is_tid x then
(Syntax.const @{syntax_const "_ofsort"} $ v $
Syntax.const @{class_syntax len})
else u
| _ => u)
fun len_float_tr [t, u] =
float (len_tr t) (len_tr u)
in
[(@{syntax_const "_floatSingleNaN"}, K len_float_tr)]
end
›
subsection ‹Value constructors›
subsubsection ‹FP literals as bit string triples, with the leading bit for the significand not
represented (hidden bit)›
lift_definition fp :: "1 word ⇒ 'e word ⇒ 'f word ⇒ ('e, 'f) floatSingleNaN"
is "λs e f. IEEE.Abs_float (s, e, f)" .
subsubsection ‹Plus and minus infinity›
lift_definition plus_infinity :: "('e, 'f) floatSingleNaN" (‹∞›) is IEEE.plus_infinity .
lift_definition minus_infinity :: "('e, 'f) floatSingleNaN" is IEEE.minus_infinity .
subsubsection ‹Plus and minus zero›
instantiation floatSingleNaN :: (len, len) zero begin
lift_definition zero_floatSingleNaN :: "('a, 'b) floatSingleNaN" is 0 .
instance ..
end
lift_definition minus_zero :: "('e, 'f) floatSingleNaN" is IEEE.minus_zero .
subsubsection ‹Non-numbers (NaN)›
lift_definition NaN :: "('e, 'f) floatSingleNaN" is some_nan .
subsection ‹Operators›
subsubsection ‹Absolute value›
setup ‹Sign.mandatory_path "abs_floatSingleNaN_inst"›
instantiation floatSingleNaN :: (len, len) abs
begin
lift_definition abs_floatSingleNaN :: "('a, 'b) floatSingleNaN ⇒ ('a, 'b) floatSingleNaN" is abs
unfolding is_nan_equivalent_def by (metis IEEE.abs_float_def is_nan_uminus)
instance ..
end
setup ‹Sign.parent_path›
subsubsection ‹Negation (no rounding needed)›
instantiation floatSingleNaN :: (len, len) uminus
begin
lift_definition uminus_floatSingleNaN :: "('a, 'b) floatSingleNaN ⇒ ('a, 'b) floatSingleNaN" is uminus
unfolding is_nan_equivalent_def by (metis is_nan_uminus)
instance ..
end
subsubsection ‹Addition›
lift_definition fadd :: "roundmode ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN" is IEEE.fadd
unfolding is_nan_equivalent_def by (metis IEEE.fadd_def)
subsubsection ‹Subtraction›
lift_definition fsub :: "roundmode ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN" is IEEE.fsub
unfolding is_nan_equivalent_def by (metis IEEE.fsub_def)
subsubsection ‹Multiplication›
lift_definition fmul :: "roundmode ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN" is IEEE.fmul
unfolding is_nan_equivalent_def by (metis IEEE.fmul_def)
subsubsection ‹Division›
lift_definition fdiv :: "roundmode ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN" is IEEE.fdiv
unfolding is_nan_equivalent_def by (metis IEEE.fdiv_def)
subsubsection ‹Fused multiplication and addition; $(x \cdot y) + z$›
lift_definition fmul_add :: "roundmode ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN" is IEEE.fmul_add
unfolding is_nan_equivalent_def by (smt (verit) IEEE.fmul_add_def)
subsubsection ‹Square root›
lift_definition fsqrt :: "roundmode ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN" is IEEE.fsqrt
unfolding is_nan_equivalent_def by (metis IEEE.fsqrt_def)
subsubsection ‹Remainder: $x - y \cdot n$, where $n \in \mathrm{Z}$ is nearest to $x/y$›
lift_definition frem :: "roundmode ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN" is IEEE.frem
unfolding is_nan_equivalent_def by (metis IEEE.frem_def)
lift_definition float_rem :: "('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN" is IEEE.float_rem
unfolding is_nan_equivalent_def by (metis IEEE.frem_def IEEE.float_rem_def)
subsubsection ‹Rounding to integral›
lift_definition fintrnd :: "roundmode ⇒ ('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN" is IEEE.fintrnd
unfolding is_nan_equivalent_def by (metis IEEE.fintrnd_def)
subsubsection ‹Minimum and maximum›
text ‹In IEEE 754-2019, the minNum and maxNum operations of the 2008 version of the standard have
been replaced by minimum, minimumNumber, maximum, maximumNumber (see Section~9.6 of the 2019
standard). These are not (yet) available in SMT-LIB. We currently do not implement any of these
operations.›
subsubsection ‹Comparison operators›
lift_definition fle :: "('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN ⇒ bool" is IEEE.fle
unfolding is_nan_equivalent_def by (smt (verit) IEEE.fcompare_def IEEE.fle_def)
lift_definition flt :: "('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN ⇒ bool" is IEEE.flt
unfolding is_nan_equivalent_def by (smt (verit) IEEE.fcompare_def IEEE.flt_def)
lift_definition fge :: "('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN ⇒ bool" is IEEE.fge
unfolding is_nan_equivalent_def by (smt (verit) IEEE.fcompare_def IEEE.fge_def)
lift_definition fgt :: "('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN ⇒ bool" is IEEE.fgt
unfolding is_nan_equivalent_def by (smt (verit) IEEE.fcompare_def IEEE.fgt_def)
subsubsection ‹IEEE 754 equality›
lift_definition feq :: "('e ,'f) floatSingleNaN ⇒ ('e ,'f) floatSingleNaN ⇒ bool" is IEEE.feq
unfolding is_nan_equivalent_def by (smt (verit) IEEE.fcompare_def IEEE.feq_def)
subsubsection ‹Classification of numbers›
lift_definition is_normal :: "('e, 'f) floatSingleNaN ⇒ bool" is IEEE.is_normal
unfolding is_nan_equivalent_def using float_distinct by blast
lift_definition is_subnormal :: "('e, 'f) floatSingleNaN ⇒ bool" is IEEE.is_denormal
unfolding is_nan_equivalent_def using float_distinct by blast
lift_definition is_zero :: "('e, 'f) floatSingleNaN ⇒ bool" is IEEE.is_zero
unfolding is_nan_equivalent_def using float_distinct by blast
lift_definition is_infinity :: "('e, 'f) floatSingleNaN ⇒ bool" is IEEE.is_infinity
unfolding is_nan_equivalent_def using float_distinct by blast
lift_definition is_nan :: "('e, 'f) floatSingleNaN ⇒ bool" is IEEE.is_nan
unfolding is_nan_equivalent_def by blast
lift_definition is_finite :: "('e, 'f) floatSingleNaN ⇒ bool" is IEEE.is_finite
unfolding is_nan_equivalent_def using nan_not_finite by blast
definition is_negative :: "('e, 'f) floatSingleNaN ⇒ bool"
where "is_negative x ≡ x = minus_zero ∨ flt x minus_zero"
definition is_positive :: "('e, 'f) floatSingleNaN ⇒ bool"
where "is_positive x ≡ x = 0 ∨ flt 0 x"
subsection ‹Conversions to other sorts›
subsubsection ‹to real›
text ‹SMT-LIB leaves {\tt fp.to\_real} unspecified for $+\infty$, $-\infty$, NaN. In contrast,
@{const valof} is (partially) specified also for those arguments. This means that the SMT-LIB
semantics can prove fewer theorems about {\tt fp.to\_real} than Isabelle can prove about
@{const valof}.›
lift_definition valof :: "('e,'f) floatSingleNaN ⇒ real"
is "λa. if IEEE.is_infinity a then undefined a
else if IEEE.is_nan a then undefined
else IEEE.valof a"
unfolding is_nan_equivalent_def using float_distinct(1) by fastforce
subsubsection ‹to unsigned machine integer, represented as a bit vector›
definition unsigned_word_of_floatSingleNaN :: "roundmode ⇒ ('e,'f) floatSingleNaN ⇒ 'a::len word"
where "unsigned_word_of_floatSingleNaN mode a ≡
if is_infinity a ∨ is_nan a then undefined mode a
else (SOME w. valof (fintrnd mode a) = real_of_word w)"
subsubsection ‹to signed machine integer, represented as a 2's complement bit vector›
definition signed_word_of_floatSingleNaN :: "roundmode ⇒ ('e,'f) floatSingleNaN ⇒ 'a::len word"
where "signed_word_of_floatSingleNaN mode a ≡
if is_infinity a ∨ is_nan a then undefined mode a
else (SOME w. valof (fintrnd mode a) = real_of_int (sint w))"
subsection ‹Conversions from other sorts›
subsubsection ‹from single bitstring representation in IEEE 754 interchange format›
text ‹The intention is that @{prop ‹LENGTH('a::len) = 1 + LENGTH('e::len) + LENGTH('f::len)›}
(recall that @{term ‹LENGTH('f::len)›} does not include the significand's hidden bit). Of course,
the type system of Isabelle/HOL is not strong enough to enforce this.›
definition floatSingleNaN_of_IEEE754_word :: "'a::len word ⇒ ('e,'f) floatSingleNaN"
where "floatSingleNaN_of_IEEE754_word w ≡
let (se, f) = word_split w :: 'a word × _; (s, e) = word_split se in fp s e f"
subsubsection ‹from real›
lift_definition round :: "roundmode ⇒ real ⇒ ('e,'f) floatSingleNaN" is IEEE.round .
subsubsection ‹from another floating point sort›
definition floatSingleNaN_of_floatSingleNaN :: "roundmode ⇒ ('a,'b) floatSingleNaN ⇒ ('e,'f) floatSingleNaN"
where "floatSingleNaN_of_floatSingleNaN mode a ≡
if a = plus_infinity then plus_infinity
else if a = minus_infinity then minus_infinity
else if a = NaN then NaN
else round mode (valof a)"
subsubsection ‹from signed machine integer, represented as a 2's complement bit vector›
definition floatSingleNaN_of_signed_word :: "roundmode ⇒ 'a::len word ⇒ ('e,'f) floatSingleNaN"
where "floatSingleNaN_of_signed_word mode w ≡ round mode (real_of_int (sint w))"
subsubsection ‹from unsigned machine integer, represented as bit vector›
definition floatSingleNaN_of_unsigned_word :: "roundmode ⇒ 'a::len word ⇒ ('e,'f) floatSingleNaN"
where "floatSingleNaN_of_unsigned_word mode w ≡ round mode (real_of_word w)"
end