diff --git a/share/libc/math.h b/share/libc/math.h index ff121459534bd3d364ed3040fdf1fd591e60cf11..ad1d732027ff5741d8137ab0f3adf98945ddb396 100644 --- a/share/libc/math.h +++ b/share/libc/math.h @@ -37,10 +37,6 @@ typedef double double_t; #define MATH_ERRNO 1 #define MATH_ERREXCEPT 2 -#define HUGE_VAL 0x1.0p2047 -#define HUGE_VALF 0x1.0p255f -#define HUGE_VALL 0x1.0p32767L - /* The constants below are not part of C99/C11 but they are defined in POSIX */ #define M_E 0x1.5bf0a8b145769p1 /* e */ #define M_LOG2E 0x1.71547652b82fep0 /* log_2 e */ @@ -755,6 +751,37 @@ extern int __finite(double d); # define isfinite(x) \ (sizeof (x) == sizeof (float) ? __finitef (x) : __finite (x)) +//The (integer x) argument is just here because a function without argument is +//applied differently in ACSL and C + +/*@ + + logic float __fc_infinity(integer x) = \plus_infinity; + logic float __fc_nan(integer x) = \NaN; + + @*/ + +/*@ + ensures \eq_float(\result,\plus_infinity); + assigns \result \from \nothing; + @*/ +extern const float __fc_infinity(int x); + +/*@ + ensures \is_NaN(\result); + assigns \result \from \nothing; + @*/ +extern const float __fc_nan(); + + +#define INFINITY __fc_infinity(0) +#define NAN __fc_nan(0) + +#define HUGE_VALF INFINITY +#define HUGE_VAL ((double)INFINITY) +#define HUGE_VALL ((long double)INFINITY) + + __END_DECLS __POP_FC_STDLIB diff --git a/src/kernel_internals/typing/logic_builtin.ml b/src/kernel_internals/typing/logic_builtin.ml index ae1a73a97132d659383b01a7ec8643a7f72c002d..7bf6f33d929adccfc01553e0efea752412d41cd2 100644 --- a/src/kernel_internals/typing/logic_builtin.ml +++ b/src/kernel_internals/typing/logic_builtin.ml @@ -285,6 +285,9 @@ let init = ["m", rounding_mode; "x", Lreal], float_type; "\\round_double", [], ["m", rounding_mode ; "x", Lreal], double_type; + "\\plus_infinity", [], [], float_type; + "\\minus_infinity", [], [], float_type; + "\\NaN", [], [], float_type; (*"\\round_quad", [], ["m", rounding_mode; "x", Lreal], long_double_type;*) diff --git a/src/kernel_services/abstract_interp/float_interval.ml b/src/kernel_services/abstract_interp/float_interval.ml index 13650e1b3183bdf9b665803d79f5d9eb61f90e05..57dc208681a05f887f6c270fb581898e6ba948ef 100644 --- a/src/kernel_services/abstract_interp/float_interval.ml +++ b/src/kernel_services/abstract_interp/float_interval.ml @@ -223,7 +223,7 @@ module Make (F: Float_sig.S) = struct let pos_zero prec = singleton (Cst.pos_zero prec) let one prec = singleton (F.of_float Near prec 1.) let pos_infinity prec = singleton (Cst.pos_infinity prec) - let _neg_infinity prec = singleton (Cst.neg_infinity prec) + let neg_infinity prec = singleton (Cst.neg_infinity prec) let minus_one_one prec ~nan = FRange.inject ~nan (F.of_float Near prec (-1.)) (F.of_float Near prec 1.) @@ -420,6 +420,20 @@ module Make (F: Float_sig.S) = struct | FRange.NaN -> Comp.False | FRange.Itv (_b, _e, nan) -> if nan then Comp.Unknown else Comp.True + let is_pos_infinity = function + | FRange.NaN -> Comp.False + | FRange.Itv (b, e, _) -> + if not (Cmp.is_pos_infinity e) then Comp.False + else if not (Cmp.is_pos_infinity b) then Comp.Unknown + else Comp.True + + let is_neg_infinity = function + | FRange.NaN -> Comp.False + | FRange.Itv (b, e, _) -> + if not (Cmp.is_neg_infinity b) then Comp.False + else if not (Cmp.is_neg_infinity e) then Comp.Unknown + else Comp.True + let is_finite = function | FRange.NaN -> Comp.False | FRange.Itv (b, e, nan) -> @@ -441,6 +455,11 @@ module Make (F: Float_sig.S) = struct | FRange.NaN -> `Bottom | FRange.Itv (b, e, _) -> `Value (FRange.inject ~nan:false b e) + let backward_is_nan = function + | FRange.Itv (_, _, false) -> `Bottom + | FRange.Itv (_, _, true) + | FRange.NaN -> `Value nan + let backward_is_finite prec = function | FRange.NaN -> `Bottom | FRange.Itv (b, e, _) as f -> diff --git a/src/kernel_services/abstract_interp/float_interval_sig.mli b/src/kernel_services/abstract_interp/float_interval_sig.mli index aee6a1d773ce8bba231de5e1445ec9b92131bcba..fe24f048e452c055057c8b912ad2d275a7a26d1b 100644 --- a/src/kernel_services/abstract_interp/float_interval_sig.mli +++ b/src/kernel_services/abstract_interp/float_interval_sig.mli @@ -47,6 +47,10 @@ module type S = sig (** The NaN singleton *) val nan: t + (** The infinities singleton *) + val pos_infinity: prec -> t + val neg_infinity: prec -> t + (** [inject ~nan b e] creates the floating-point interval [b..e], plus NaN if [nan] is true. [b] and [e] must be ordered, and not NaN. They can be infinite. *) @@ -87,8 +91,11 @@ module type S = sig val is_finite: t -> Abstract_interp.Comp.result val is_not_nan: t -> Abstract_interp.Comp.result + val is_pos_infinity: t -> Abstract_interp.Comp.result + val is_neg_infinity: t -> Abstract_interp.Comp.result val backward_is_finite: prec -> t -> t or_bottom val backward_is_not_nan: t -> t or_bottom + val backward_is_nan: t -> t or_bottom (** [has_greater_min_bound f1 f2] returns 1 if the interval [f1] has a better minimum bound (i.e. greater) than the interval [f2]. *) diff --git a/src/plugins/value/legacy/eval_terms.ml b/src/plugins/value/legacy/eval_terms.ml index ce9a20a55a312fa437321e4c9a52affec6a9aa2e..f5e973e525dd461f18bf36ac3eeecf5453a9bfbe 100644 --- a/src/plugins/value/legacy/eval_terms.ml +++ b/src/plugins/value/legacy/eval_terms.ml @@ -633,6 +633,9 @@ let known_logic_funs = [ "\\sign", ACSL; "\\min", ACSL; "\\max", ACSL; + "\\plus_infinity", ACSL; + "\\minus_infinity", ACSL; + "\\NaN", ACSL; ] let known_predicates = [ "\\warning", ACSL; @@ -745,6 +748,16 @@ let rec eval_term ~alarm_mode env t = (* Special case for the constants \pi and \e. *) | TLval (TVar {lv_name = "\\pi"}, _) -> ereal (V.inject_float Fval.pi) | TLval (TVar {lv_name = "\\e"}, _) -> ereal (V.inject_float Fval.e) + | TLval (TVar {lv_name = "\\plus_infinity"}, _) -> + let v = Cvalue.V.inject_float (Fval.pos_infinity Float_sig.Single) in + { etype = Cil.floatType; eunder = v; eover = v; ldeps = empty_logic_deps } + | TLval (TVar {lv_name = "\\minus_infinity"}, _) -> + let v = Cvalue.V.inject_float (Fval.neg_infinity Float_sig.Single) in + { etype = Cil.floatType; eunder = v; eover = v; ldeps = empty_logic_deps } + | TLval (TVar {lv_name = "\\NaN"}, _) -> + let v = Cvalue.V.inject_float Fval.nan in + { etype = Cil.floatType; eunder = v; eover = v; ldeps = empty_logic_deps } + | TLval _ -> let lval = eval_tlval ~alarm_mode env t in let typ = lval.etype in @@ -1220,6 +1233,7 @@ and eval_tif : 'a. (alarm_mode:_ -> _ -> _ -> 'a eval_result) -> ('a -> 'a -> 'a | false, false -> assert false (* a logic alarm would have been raised*) (* if you add something here, update known_logic_funs above also *) +(* Constants are directly in eval_terms *) and eval_known_logic_function ~alarm_mode env li labels args = let lvi = li.l_var_info in match lvi.lv_name, li.l_type, labels, args with @@ -1271,7 +1285,6 @@ and eval_known_logic_function ~alarm_mode env li labels args = | "\\max", Some Lreal, _, [t1; t2] -> let backward = Cvalue.V.backward_comp_float_left_true Comp.Ge Fval.Real in eval_extremum Cil.doubleType backward ~alarm_mode env t1 t2 - | _ -> assert false and eval_float_builtin_arity2 ~alarm_mode env name arg1 arg2 = @@ -1694,6 +1707,7 @@ let reduce_by_left_relation ~alarm_mode env positive tl rel tr = let debug = false in if debug then Format.printf "#Left term %a@." Printer.pp_term tl; let typ_loc, locs = eval_term_as_exact_locs ~alarm_mode env tl in + if debug then Format.printf "HERE@."; let reduce = Eval_op.backward_comp_left_from_type typ_loc in let rtl = eval_term ~alarm_mode env tr in let cond_v = rtl.eover in @@ -1747,40 +1761,40 @@ let rec reduce_by_relation ~alarm_mode env positive t1 rel t2 = May raise LogicEvalError or Not_an_exact_loc, when no reduction can be done, and Reduce_to_bottom, in which case the reduction leads to bottom. *) let reduce_by_known_papp ~alarm_mode env positive li _labels args = - match positive, li.l_var_info.lv_name, args with - | true, "\\is_finite", [arg] - | false, "\\is_NaN", [arg] -> begin - let fval_reduce = - (* positive is true for is_finite, false for is_NaN. *) - if positive - then Fval.backward_is_finite - else (fun _fkind -> Fval.backward_is_not_nan) - in - try - let alarm_mode = alarm_reduce_mode () in - let typ_loc, locs = eval_term_as_exact_locs ~alarm_mode env arg in - let aux loc env = - let state = env_current_state env in - let v = find_or_alarm ~alarm_mode state loc in - let v = Cvalue_forward.reinterpret typ_loc v in - let v = match Cil.unrollType typ_loc with - | TFloat (fkind,_) -> begin - let v = Cvalue.V.project_float v in - let kind = Fval.kind fkind in - match fval_reduce kind v with - | `Value f -> V.inject_float f - | `Bottom -> V.bottom - end - | _ -> (* Better safe than sorry, we may have e.g. en int location - here *) - raise Not_an_exact_loc - in - let state' = Cvalue.Model.reduce_previous_binding state loc v in - overwrite_current_state env state' + let reduce_float fval_reduce arg = + try + let alarm_mode = alarm_reduce_mode () in + let typ_loc, locs = eval_term_as_exact_locs ~alarm_mode env arg in + let aux loc env = + let state = env_current_state env in + let v = find_or_alarm ~alarm_mode state loc in + let v = Cvalue_forward.reinterpret typ_loc v in + let v = match Cil.unrollType typ_loc with + | TFloat (fkind,_) -> begin + let v = Cvalue.V.project_float v in + let kind = Fval.kind fkind in + match fval_reduce kind v with + | `Value f -> V.inject_float f + | `Bottom -> V.bottom + end + | _ -> (* Better safe than sorry, we may have e.g. en int location + here *) + raise Not_an_exact_loc in - Eval_op.apply_on_all_locs aux locs env - with Cvalue.V.Not_based_on_null -> env - end + let state' = Cvalue.Model.reduce_previous_binding state loc v in + overwrite_current_state env state' + in + Eval_op.apply_on_all_locs aux locs env + with LogicEvalError _ | Not_an_exact_loc | Cvalue.V.Not_based_on_null -> + env + in + match positive, li.l_var_info.lv_name, args with + | true, "\\is_finite", [arg] -> + reduce_float Fval.backward_is_finite arg + | false, "\\is_NaN", [arg] -> + reduce_float (fun _fkind -> Fval.backward_is_not_nan) arg + | true, "\\is_NaN", [arg] -> + reduce_float (fun _fkind -> Fval.backward_is_nan) arg | _ , ("\\eq_float" | "\\eq_double"), [t1;t2] -> reduce_by_relation ~alarm_mode env positive t1 Req t2 | _ , ("\\ne_float" | "\\ne_double"), [t1;t2] -> @@ -2289,6 +2303,8 @@ and eval_predicate env pred = Value_parameters.abort "Wrong argument: \\warning expects a constant string" end + | "\\is_plus_infinity", [arg] -> unary_float Fval.is_pos_infinity arg + | "\\is_minus_infinity", [arg] -> unary_float Fval.is_neg_infinity arg | "\\subset", [argl;argr] -> begin try let l = eval_term ~alarm_mode env argl in diff --git a/tests/value/INFINITY_NAN.c b/tests/value/INFINITY_NAN.c new file mode 100644 index 0000000000000000000000000000000000000000..7b12350993d730cad7ea2414cb85298617a5a444 --- /dev/null +++ b/tests/value/INFINITY_NAN.c @@ -0,0 +1,39 @@ +/* run.config* + STDOPT: +"-warn-special-float none" +*/ + +#include <math.h> + +int main1(){ + float infinity_f = INFINITY; + Frama_C_show_each(infinity_f); + /*@ assert \eq_float(infinity_f,INFINITY); @*/ + double infinity_d = HUGE_VAL; + if(infinity_f == infinity_d) { + return 1; + } else { + /*@ assert \false; @*/ + return 0; + } +} + + +volatile float nondet; + +int main2(){ + float nan_f = nondet; + Frama_C_show_each(nan_f); + /*@ assert \is_NaN(nan_f); @*/ + double infinity_d = HUGE_VAL; + if(nan_f == infinity_d) { + /*@ assert \false; @*/ + return 0; + } else { + return 1; + } +} + + +int main(){ + return (main1 () && main2 ()); +} diff --git a/tests/value/oracle/INFINITY_NAN.res.oracle b/tests/value/oracle/INFINITY_NAN.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..d18e3877a6ccd75da4ce8ce953bdeec0fa09f912 --- /dev/null +++ b/tests/value/oracle/INFINITY_NAN.res.oracle @@ -0,0 +1,6 @@ +[eva] Warning: Unknown message key summary +[kernel] Parsing tests/value/INFINITY_NAN.c (with preprocessing) +[kernel] tests/value/INFINITY_NAN.c:8: Failure: Cannot resolve variable INFINITY +[kernel] User Error: stopping on file "tests/value/INFINITY_NAN.c" that has errors. Add + '-kernel-msg-key pp' for preprocessing command. +[kernel] Frama-C aborted: invalid user input.