1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159(**************************************************************************)(* This file is part of the Codex semantics library. *)(* *)(* Copyright (C) 2013-2025 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* you can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1. *)(* *)(* It is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file LICENSE). *)(* *)(**************************************************************************)moduleComp=Abstract_interp.CompopenBottom.Typetyperound=Float_sig.round=Up|Down|Near|Zerotypeprec=Float_sig.prec=Single|Double|Long_Double|RealmoduleMake(F:Float_sig.S)=struct(* Definitions of useful floating-point constants. *)moduleCst=structletpos_zeroprec=F.of_floatNearprec0.letneg_zeroprec=F.of_floatNearprec(-0.)letpos_infinityprec=F.of_floatNearprecinfinityletneg_infinityprec=F.of_floatNearprecneg_infinity(* Maximum value M such that all integers 0 < n <= M are exactly
representable in the given precision.
It is 2^53 for double and 2^24 for single precision. *)letmax_precise_integerprec=letf=matchprecwith|Single->2.**24.|Double|Long_Double->2.**53.|Real->infinityinF.of_floatNearprecf(* Maximum odd integer representable in the given precision.
It is 2^53-1 for double and 2^24-1 for single precision. *)letmax_odd_integerprec=letf=matchprecwith|Single->2.**24.-.1.|Double->2.**53.-.1.|Real|Long_Double->infinityinF.of_floatNearprecf(* Successor of the maximum non-integer representable in the given precision.
It is 2^52 in double and 2^23 in single precision. *)letceil_of_max_non_integerprec=letf=matchprecwith|Single->2.**23.|Double->2.**52.|Real|Long_Double->infinityinF.of_floatNearprecf(* Returns 1.0 if [f] is positive, and -1.0 if [t] is negative.
[f] can be infinite or a (positive or negative) zero, but not NaN. *)letsignprecf=lets=ifF.is_negativefthen-1.else1.inF.of_floatNearprecsend(* IEEE and non-IEEE comparisons. *)moduleCmp=struct(* Constants in single precision: should be used only for comparisons,
and not to create intervals of arbitrary precisions. *)letpos_zero=Cst.pos_zeroFloat_sig.Singleletneg_zero=Cst.neg_zeroFloat_sig.Singleletone=F.of_floatNearFloat_sig.Single1.letpos_infinity=Cst.pos_infinityFloat_sig.Singleletneg_infinity=Cst.neg_infinityFloat_sig.Singleletequal_ieeef1f2=F.cmp_ieeef1f2=0letle_ieeef1f2=F.cmp_ieeef1f2<=0letlt_ieeef1f2=F.cmp_ieeef1f2<0letge_ieeef1f2=F.cmp_ieeef1f2>=0letgt_ieeef1f2=F.cmp_ieeef1f2>0letis_a_zerof=equal_ieeepos_zerofletis_pos_infinityf=equal_ieeepos_infinityfletis_neg_infinityf=equal_ieeeneg_infinityfletequalf1f2=F.comparef1f2=0letlef1f2=F.comparef1f2<=0letltf1f2=F.comparef1f2<0letgef1f2=F.comparef1f2>=0letgtf1f2=F.comparef1f2>0letminf1f2=iflef1f2thenf1elsef2letmaxf1f2=iflef1f2thenf2elsef1(* next_float_ieee -0. = next_float_ieee 0. *)letnext_float_ieeeprecf=letf=ifequalfneg_zerothenCst.pos_zeroprecelsefinF.next_floatprecf(* prev_float_ieee 0. = next_float_ieee -0. *)letprev_float_ieeeprecf=letf=ifequalfpos_zerothenCst.neg_zeroprecelsefinF.prev_floatprecfendmoduleFRange=struct(* invariants for intervals Itv (b, e, nan):
- b and e are not NaN;
- b <= e *)typet=|ItvofF.t*F.t*bool|NaNletnan=NaNletinject?(nan=false)be=ifF.is_nanb||F.is_nane||F.comparebe>0thenCodex_log.fatal"Invalid bounds for float interval:@ %a .. %a@."F.prettybF.prettye;Itv(b,e,nan)letinject_after_tightenprec~nanbe=letb=F.round_to_precisionUpprecbande=F.round_to_precisionDownpreceinifCmp.lebethen`Value(inject~nanbe)elseifnanthen`ValueNaNelse`Bottomletadd_nan=function|NaNasf->f|Itv(_,_,true)asf->f|Itv(b,e,false)->Itv(b,e,true)letadd_pos_infinityprect=letpos_inf=Cst.pos_infinityprecinmatchtwith|NaN->Itv(pos_inf,pos_inf,true)|Itv(b,_e,nan)->Itv(b,pos_inf,nan)letadd_neg_infinityprect=letneg_inf=Cst.neg_infinityprecinmatchtwith|NaN->Itv(neg_inf,neg_inf,true)|Itv(_b,e,nan)->Itv(neg_inf,e,nan)end(* ------------------------------------------------------------------------
Datatype
------------------------------------------------------------------------ *)typet=FRange.t(* let structural_descr =
* Structural_descr.t_sum [|
* [| F.packed_descr; F.packed_descr; Structural_descr.p_bool |];
* [| |]
* |]
* let packed_descr = Structural_descr.pack structural_descr *)letcomparexy=matchx,ywith|FRange.Itv(b1,e1,n1),FRange.Itv(b2,e2,n2)->letc=Stdlib.comparen1n2inifc<>0thencelseletr=F.compareb1b2inifr<>0thenrelseF.comparee1e2|FRange.Itv_,FRange.NaN->-1|FRange.NaN,FRange.Itv_->1|FRange.NaN,FRange.NaN->0letequalxy=matchx,ywith|FRange.Itv(b1,e1,n1),FRange.Itv(b2,e2,n2)->Cmp.equalb1b2&&Cmp.equale1e2&&n1=n2|FRange.NaN,FRange.NaN->true|_->falseletpretty_or_zerofmtf=ifCmp.(equalfpos_zero)thenFormat.fprintffmt"0"elseF.prettyfmtfletprettyfmt=function|FRange.Itv(b,e,nan)->ifCmp.equalbethenFormat.fprintffmt"{%a%t}"pretty_or_zerob(funfmt->ifnanthenFormat.pp_print_stringfmt";NaN")elseletprint_nanfmtnan=ifnanthenFormat.fprintffmt" %s {NaN}"("U"(* Unicode.union_string ()*))inFormat.fprintffmt"[%a .. %a]%a"F.prettybF.prettyeprint_nannan|FRange.NaN->Format.fprintffmt"NaN"lethash=function|FRange.Itv(b,e,n)->(2*F.hashb)+(5*F.hashe)+(7*Hashtbl.hashn)|FRange.NaN->3(* ------------------------------------------------------------------------
Constants, injections, projections
------------------------------------------------------------------------ *)letmin_and_max=function|FRange.Itv(b,e,nan)->Some(b,e),nan|FRange.NaN->None,trueletnan=FRange.nanletinject=FRange.injectletsingletonx=FRange.injectxxletpos_zeroprec=singleton(Cst.pos_zeroprec)letoneprec=singleton(F.of_floatNearprec1.)letpos_infinityprec=singleton(Cst.pos_infinityprec)let_neg_infinityprec=singleton(Cst.neg_infinityprec)letminus_one_oneprec~nan=FRange.inject~nan(F.of_floatNearprec(-1.))(F.of_floatNearprec1.)letm_pi=3.1415929794311523(* single-precision *)letm_minus_pi=-.m_piletminus_pi_piprec~nan=FRange.inject~nan(F.of_floatNearprecm_minus_pi)(F.of_floatNearprecm_pi)lettop_fprec~nan=FRange.inject~nan(Cst.neg_infinityprec)(Cst.pos_infinityprec)lettopprec=top_fprec~nan:truelettop_finiteprec=ifF.is_exactprecthenletb=F.next_floatprec(Cst.neg_infinityprec)inlete=F.prev_floatprec(Cst.pos_infinityprec)inFRange.inject~nan:falsebeelsetop_fprec~nan:false(* Splits the interval [b; e] into two intervals (neg, pos) such that [neg]
(resp. [pos]) contains only negative (resp. positive) floats. *)letsplit_by_signprec((b,e)asx)=ifCmp.(leeneg_zero)then`Valuex,`BottomelseifCmp.(gebpos_zero)then`Bottom,`Valuexelse`Value(b,Cst.neg_zeroprec),`Value(Cst.pos_zeroprec,e)(* Returns true iff [f] represents an odd integer. *)letis_oddprecf=lettwo=F.of_floatNearprec2.inCmp.equal(F.abs(F.fmodNearprecftwo))Cmp.one(* [split_by_parity (b, e)] returns [even_bounds, odd_bounds], where:
- [even_bounds] are the min and max even integers enclosed between b and e
(or None if there is no such even integer).
- [odd_bounds] are the min and max odd integers (representable as floating-
point values) enclosed between b and e (or None if there is no such odd
integer). These bounds cannot be infinite. *)letsplit_by_parityprec(b,e)=letb=F.ceilbande=F.flooreinifCmp.gt_ieeebethenNone,NoneelseifCmp.equal_ieeebethenletx=(b,e)inifis_oddprecbthenNone,SomexelseSomex,Noneelseletone=F.of_floatNearprec1.inletmin_even,min_odd=ifis_oddprecb(* No rounding errors may happen below because odd numbers are bounded. *)thenF.addNearprecbone,belseb,Cmp.max(F.neg(Cst.max_odd_integerprec))(F.addNearprecbone)andmax_even,max_odd=ifis_oddprece(* No rounding errors may happen below because odd numbers are bounded. *)thenF.subNearpreceone,eelsee,Cmp.min(Cst.max_odd_integerprec)(F.subNearpreceone)inleteven=Some(min_even,max_even)andodd=ifCmp.lt_ieeemax_oddmin_oddthenNoneelseSome(min_odd,max_odd)ineven,odd(* -----------------------------------------------------------------------
Lattice
----------------------------------------------------------------------- *)letis_includedx1x2=matchx1,x2with|FRange.Itv(b1,e1,n1),FRange.Itv(b2,e2,n2)->F.compareb2b1<=0&&F.comparee1e2<=0&&(notn1||n2)|FRange.NaN,FRange.Itv(_,_,true)->true|FRange.NaN,FRange.NaN->true|_->falseletjoinf1f2=matchf1,f2with|FRange.Itv(b1,e1,n1),FRange.Itv(b2,e2,n2)->FRange.inject~nan:(n1||n2)(Cmp.minb1b2)(Cmp.maxe1e2)|(FRange.Itv(b1,e1,_),FRange.NaN)|(FRange.NaN,FRange.Itv(b1,e1,_))->FRange.inject~nan:trueb1e1|FRange.NaN,FRange.NaN->FRange.nanletwiden_downf=F.neg(F.widen_up(F.negf))letwidenf1f2=assert(is_includedf1f2);matchf1,f2with|FRange.Itv(b1,e1,_),FRange.Itv(b2,e2,nan)->letb=ifCmp.equalb2b1thenb2elsewiden_downb2inlete=ifCmp.equale2e1thene2elseF.widen_upe2in(** widen_up and down produce double only if the input is a double *)FRange.inject~nanbe|FRange.NaN,f2->f2|FRange.Itv_,FRange.NaN->assertfalseletmeetf1f2=matchf1,f2with|FRange.Itv(b1,e1,n1),FRange.Itv(b2,e2,n2)->letis_finite=F.compareb2e1<=0&&F.compareb1e2<=0inletis_nan=n1&&n2inifis_finite||is_nanthenletv=ifis_finitethenFRange.inject~nan:is_nan(Cmp.maxb1b2)(Cmp.mine1e2)elseFRange.nanin`Valuevelse`Bottom|(FRange.Itv(_,_,true)|FRange.NaN),(FRange.Itv(_,_,true)|FRange.NaN)->`ValueFRange.nan|_->`Bottomletnarrow=meet(* ------------------------------------------------------------------------
Tests
------------------------------------------------------------------------ *)letcontainscst=function|FRange.NaN->false|FRange.Itv(b,e,_)->Cmp.lebcst&&Cmp.geecstletcontains_pos_zerot=containsCmp.pos_zerotletcontains_neg_zerot=containsCmp.neg_zerotletcontains_a_zero=function|FRange.Itv(b,e,_)->Cmp.(le_ieeebpos_zero)&&Cmp.(ge_ieeeepos_zero)|FRange.NaN->falseletcontains_non_zero=function|FRange.Itv(b,e,nan)->nan||Cmp.(lt_ieeebpos_zero)||Cmp.(gt_ieeeepos_zero)|FRange.NaN->trueletcontains_strictly_pos=function|FRange.Itv(_,e,_)->Cmp.(gt_ieeeepos_zero)|FRange.NaN->falseletcontains_strictly_neg=function|FRange.Itv(b,_,_)->Cmp.(lt_ieeebneg_zero)|FRange.NaN->falseletcontains_strict_neg_finite(b,e)=Cmp.(gt_ieeeeneg_infinity)&&Cmp.(ltbneg_zero)letcontains_finite_nonintegerprec(b,e)=letib=F.ceilbinnot((Cmp.equal_ieeeibb&&Cmp.equal_ieeebe)||Cmp.le_ieeee(F.neg(Cst.ceil_of_max_non_integerprec))||Cmp.ge_ieeeb(Cst.ceil_of_max_non_integerprec))letcontains_pos_infinity=function|FRange.Itv(_,e,_)->Cmp.is_pos_infinitye|FRange.NaN->falseletcontains_neg_infinity=function|FRange.Itv(b,_,_)->Cmp.is_neg_infinityb|FRange.NaN->falseletcontains_infinityf=contains_pos_infinityf||contains_neg_infinityfletcontains_nan=function|FRange.NaN->true|FRange.Itv(_,_,nan)->nanletis_singleton=function|FRange.NaN->true|FRange.Itv(b,e,nan)->Cmp.equalbe&¬nanletis_one=function|FRange.NaN->false|FRange.Itv(b,e,nan)->Cmp.(equalbone)&&Cmp.(equaleone)&¬nanletis_a_zero=function|FRange.NaN->false|FRange.Itv(b,e,nan)->notnan&&Cmp.is_a_zerob&&Cmp.is_a_zeroeletif_not_nan=function|FRange.NaN->assertfalse|FRange.Itv(b,e,_)->b,eletis_not_nan=function|FRange.NaN->Comp.False|FRange.Itv(_b,_e,nan)->ifnanthenComp.UnknownelseComp.Trueletis_finite=function|FRange.NaN->Comp.False|FRange.Itv(b,e,nan)->ifCmp.is_neg_infinitye||Cmp.is_pos_infinitybthenComp.Falseelseifnan||Cmp.is_neg_infinityb||Cmp.is_pos_infinityethenComp.UnknownelseComp.Trueletis_negative=function|FRange.Itv(b,e,false)->ifF.is_negativeethenComp.Trueelseifnot(F.is_negativeb)thenComp.FalseelseComp.Unknown|FRange.Itv(_,_,true)|FRange.NaN->Comp.Unknownletbackward_is_not_nan=function|FRange.NaN->`Bottom|FRange.Itv(b,e,_)->`Value(FRange.inject~nan:falsebe)letbackward_is_finiteprec=function|FRange.NaN->`Bottom|FRange.Itv(b,e,_)asf->ifCmp.equalbe&&F.is_infinitebthen`Bottom(* [f] is exactly an infinite, we can return `Bottom even
in the [Real] case *)elsenarrow(top_finiteprec)flethas_greater_min_boundt1t2=matcht1,t2with|FRange.Itv(m1,_,_),FRange.Itv(m2,_,_)->F.comparem1m2|FRange.NaN,FRange.Itv_->1|FRange.Itv_,FRange.NaN->-1|FRange.NaN,FRange.NaN->0lethas_smaller_max_boundt1t2=matcht1,t2with|FRange.Itv(_,m1,_),FRange.Itv(_,m2,_)->F.comparem2m1|FRange.NaN,FRange.Itv_->1|FRange.Itv_,FRange.NaN->-1|FRange.NaN,FRange.NaN->0(* ------------------------------------------------------------------------
Comparisons
------------------------------------------------------------------------ *)letforward_eq(b1,e1)(b2,e2)=letnot_intersects=Cmp.lt_ieeee2b1||Cmp.lt_ieeee1b2inifnot_intersectsthenComp.FalseelseifCmp.equal_ieeeb1e1&&Cmp.equal_ieeeb2e2thenComp.TrueelseComp.Unknownletforward_le(b1,e1)(b2,e2)=ifCmp.le_ieeee1b2thenComp.TrueelseifCmp.lt_ieeee2b1thenComp.FalseelseComp.Unknownletforward_lt(b1,e1)(b2,e2)=ifCmp.lt_ieeee1b2thenComp.TrueelseifCmp.le_ieeee2b1thenComp.FalseelseComp.Unknownletforward_compopf1f2=matchf1,f2with|FRange.NaN,_|_,FRange.NaN->ifop=Comp.NethenComp.TrueelseComp.False|FRange.Itv(b1,e1,nan1),FRange.Itv(b2,e2,nan2)->letr=matchopwith|Comp.Le->forward_le(b1,e1)(b2,e2)|Comp.Ge->forward_le(b2,e2)(b1,e1)|Comp.Lt->forward_lt(b1,e1)(b2,e2)|Comp.Gt->forward_lt(b2,e2)(b1,e1)|Comp.Eq->forward_eq(b1,e1)(b2,e2)|Comp.Ne->Abstract_interp.inv_truth(forward_eq(b1,e1)(b2,e2))inifnan1||nan2thenifop=Comp.Nethen(matchrwithComp.True->Comp.True|_->Comp.Unknown)else(matchrwithComp.False->Comp.False|_->Comp.Unknown)elser(* This function intentionally returns different results with
[e2 = -0.] and [e2 = 0.] *)letbackward_le_auxprec(b1,e1)e2=ifnot(Cmp.leb1e2)then`BottomelseifCmp.lee1e2then`Value(FRange.injectb1e1)elseFRange.inject_after_tightenprec~nan:falseb1e2(* This is the "real" backward transformer for [le], which does not distinguish
[0.] and [-0.]. Thus we enlarge the bound in the "worst" direction. *)letbackward_leprec(b1,e1)e2=lete2=ifCmp.is_a_zeroe2thenCst.pos_zeroprecelsee2inbackward_le_auxprec(b1,e1)e2letbackward_ltprec((b1,e1)asf1)e2=ifCmp.le_ieeee2b1then`BottomelseifF.is_exactprec||Cmp.equalb1e1thenbackward_leprecf1(Cmp.prev_float_ieeeprece2)else(* On real we cannot be more precise than [le], except on zeros: at
least get rid of the "bad" zero *)lete2=ifCmp.is_a_zeroe2thenCst.neg_zeroprecelsee2inbackward_le_auxprecf1e2(* see comments in {!backward_le_aux} *)letbackward_ge_auxprec(b1,e1)b2=ifnot(Cmp.leb2e1)then`BottomelseifCmp.leb2b1then`Value(FRange.injectb1e1)elseFRange.inject_after_tightenprec~nan:falseb2e1(* see comments in {!backward_le} *)letbackward_geprec(b1,e1)b2=letb2=ifCmp.is_a_zerob2thenCst.neg_zeroprecelseb2inbackward_ge_auxprec(b1,e1)b2(* see comments in {!backward_gt} *)letbackward_gtprec((b1,e1)asf1)b2=ifCmp.le_ieeee1b2then`BottomelseifF.is_exactprec||Cmp.equalb1e1thenbackward_geprecf1(Cmp.next_float_ieeeprecb2)elseletb2=ifCmp.is_a_zerob2thenCst.pos_zeroprecelseb2inbackward_ge_auxprecf1b2(** The operands cannot be {!Nan} *)letbackward_comp_left_true_finiteopprecf1'f2'=letf1=if_not_nanf1'inlet(b2,e2)=if_not_nanf2'inmatchopwith|Comp.Le->backward_leprecf1e2|Comp.Ge->backward_geprecf1b2|Comp.Lt->backward_ltprecf1e2|Comp.Gt->backward_gtprecf1b2|Comp.Eq->(* -0 and +0 must not be distinguished here *)letf2=ifcontains_a_zerof2'thenjoinf2'(FRange.inject(Cst.neg_zeroprec)(Cst.pos_zeroprec))elsef2'innarrowf1'f2|Comp.Ne->(* compute (f1 ∩ [-infty,min[ ) ∪ (f1 ∩ ]max,infty]) *)letbefore_or_afterminmax=Bottom.joinjoin(backward_ltprecf1min)(backward_gtprecf1max)in(* As usual, we cannot reduce if [f2] is not a singleton, except that
the two zeros are a kind of singleton. Checking whether [f2] is on
a frontier of [f1] is not obvious because of the multiple cases
(and [allmodes]) so we use the transformers for [lt] instead. *)ifis_a_zerof2'thenbefore_or_after(Cst.neg_zeroprec)(Cst.pos_zeroprec)elseifis_singletonf2'thenbefore_or_afterb2b2else`Valuef1'(* Applies [backward f1 f2] and removes NaN from [f1] and [f2]. *)letbackward_comp_no_nanbackward_finitef1f2=matchf1,f2with|FRange.NaN,_|_,FRange.NaN->`Bottom|FRange.Itv(b,e,nan),FRange.Itv_->letf1=ifnanthenFRange.inject~nan:falsebeelsef1inbackward_finitef1f2(* Applies [backward f1 f2] but preserves NaN from [f1] and [f2]. *)letbackward_comp_with_nanbackward_finitef1f2=ifcontains_nanf2then`Valuef1elsematchf1with|FRange.NaN->`Valuef1|FRange.Itv(_,_,nan)->letnan=ifnanthen`ValueFRange.nanelse`BottominBottom.joinjoin(backward_finitef1f2)nanletbackward_comp_left_trueopprec=letbackward_finite=backward_comp_left_true_finiteopprecinifop=Comp.Nethenbackward_comp_with_nanbackward_finiteelsebackward_comp_no_nanbackward_finiteletbackward_comp_left_falseopprec=letbackward_finite=backward_comp_left_true_finite(Comp.invop)precinifop=Comp.Nethenbackward_comp_no_nanbackward_finiteelsebackward_comp_with_nanbackward_finite(* ------------------------------------------------------------------------
Simple arithmetic operations
------------------------------------------------------------------------ *)(* The functions defined using [exact_aux] below are, among other
properties, (1) exact (the result as a real can always be
represented exactly, in the good type), and (2) total. In
particular, given a float 'x', 'ff x == (float)(f (double)x)'.
Thus, in this module, the 'f' functions are also the non-f
(since float32 are represented using double) *)let(>>)tf=matchtwith|FRange.NaN->t|FRange.Itv(b,e,nan)->FRange.inject~nan(fb)(fe)letfloort=t>>F.floorletceilt=t>>F.ceillettrunct=t>>F.truncletfroundt=t>>F.froundletneg=function|FRange.Itv(b,e,nan)->(* do not round because exact operation *)FRange.inject~nan(F.nege)(F.negb)|FRange.NaN->FRange.nanletabsprec=function|FRange.Itv(b,e,nan)asf->ifcontainsCmp.pos_zerofthenletzero=Cst.pos_zeroprecinFRange.inject~nanzero(Cmp.max(F.absb)(F.abse))else(* f is either strictly positive or strictly negative *)ifF.compareeCmp.pos_zero<0thennegfelsef|FRange.NaNasf->f(* This monad returns a NaN if one operand can only be NaN, and lets the
second function perform the computation if both operands contain a
non-empty floating-point interval. *)let(>>%)=fun(x,y)f->matchx,ywith|FRange.NaN,_|_,FRange.NaN->FRange.nan|FRange.Itv(b1,e1,nan1),FRange.Itv(b2,e2,nan2)->letnan=nan1||nan2inf~nan(b1,e1)(b2,e2)(* Auxiliary function used for the forward semantics of add, mul and div.
For a monotonic function [op], the bounds of [[b1..e1] op [b2..e2]] are the
minimum and maximum of [b1 op b2], [b1 op e2], [e1 op b2] and [e1 op e2].
NaN can be created from \infty - \infty, 0 * \infty, 0/0 and \infty /
\infty, in which case the result contains NaN, and new operations are
performed to take into account the results of values near \infty and 0.
Beware that NaN and discontinuities occuring between the bounds of the
arguments (i.e. on zeros, as an infinity is always a bound) should be
checked and processed by the caller. *)letmonotonicopprecxy=(x,y)>>%fun~nan(b1,e1)(b2,e2)->letnan=refnanin(* Results of [op] applied to the bounds of the intervals, excluding NaN. *)letresults=ref[]in(* When [a op b = NaN], performs new operations to take into account values
near [a] (and near [b] with the same reasoning). For such a NaN from add,
mul or div, [c op b] is constant for all values c <> a of the same sign.
Thus, we can replace [a] by any of these values.
- if [x] is a singleton or [-0 .. +0], there are no values other than the
bounds to take into account;
- otherwise, if [a] is infty, replace it by 1 with the sign of [a]
(no risk of NaN with 1 on add, mul and div);
- otherwise, if [a] is zero, the other bound [c] of the interval has the
same sign as the values near [a] in the interval; as [c op b] is also
computed, no need to perform a new operation. *)lettreat_nan_resultrndab=nan:=true;ifF.is_infinitea&¬(Cmp.equalb1e1)thenresults:=oprndprec(Cst.signpreca)b::!results;ifF.is_infiniteb&¬(Cmp.equalb2e2)thenresults:=oprndpreca(Cst.signprecb)::!results;inletoprndxy=letr=oprndprecxyinifF.is_nanrthentreat_nan_resultrndxyelseresults:=r::!resultsinletcomputernd=results:=[];oprnde1e2;oprnde1b2;oprndb1e2;oprndb1b2;inletrounding_mode=ifF.is_exactprecthenNearelseDownincomputerounding_mode;letpos_inf=Cst.pos_infinityprecinletmin=List.fold_leftCmp.minpos_inf!resultsinifnot(F.is_exactprec)thencomputeUp;letneg_inf=Cst.neg_infinityprecinletmax=List.fold_leftCmp.maxneg_inf!resultsinifmin>maxthen(assert!nan;FRange.nan)elseFRange.inject~nan:!nanminmaxletaddprec=monotonicF.addprecletsubprec=monotonicF.subprecletmulprecxy=letr=monotonicF.mulprecxyin(* A NaN may occur between the bounds of the intervals, on 0 * \infty. *)if(contains_infinityx&&contains_a_zeroy)||(contains_infinityy&&contains_a_zerox)thenFRange.add_nanrelserletdivprecxy=letr=monotonicF.divprecxyin(* A NaN may occur between the bounds of the intervals, on 0/0. *)letnan=(contains_a_zerox&&contains_a_zeroy)in(* Treat the discontinuity around 0: divisions by 0 produce infinites. *)letpos_inf=containsCmp.pos_zeroy&&contains_strictly_posx||containsCmp.neg_zeroy&&contains_strictly_negxandneg_inf=containsCmp.pos_zeroy&&contains_strictly_negx||containsCmp.neg_zeroy&&contains_strictly_posxinletr=ifpos_infthenFRange.add_pos_infinityprecrelserinletr=ifneg_infthenFRange.add_neg_infinityprecrelserinifnanthenFRange.add_nanrelser(* Could be improved a lot, cf [Marre10]. *)letbackward_add_oneprec~other~result=(* No reduction when the result contains an infinity, and when the result and
the other operand contain NaN (as x + NaN = NaN for any x). *)ifcontains_infinityresult||(contains_nanother&&contains_nanresult)then`Value(topprec)else(* Values that can lead to NaN in the result. *)letreduce_for_nant=lett=ifcontains_pos_infinityotherthenFRange.add_neg_infinityprectelsetinifcontains_neg_infinityotherthenFRange.add_pos_infinityprectelsetinletreduced_for_nan=ifcontains_nanresultthen`Value(reduce_for_nanFRange.nan)else`Bottomin(* Values that can lead to finite values in the result. *)letreduced_for_finite_values=matchresult,otherwith|FRange.NaN,_|_,FRange.NaN->`Bottom|FRange.Itv(bres,eres,_),FRange.Itv(bother,eother,_)->letbres=Cmp.prev_float_ieeeprecbresinleteres=Cmp.next_float_ieeepreceresinletround=ifF.is_exactprecthenUpelseNearinletb=F.subroundprecbreseotherinletround=ifF.is_exactprecthenDownelseNearinlete=F.subroundpreceresbotherinifCmp.lebethen`Value(FRange.inject~nan:falsebe)else`BottominBottom.joinjoinreduced_for_finite_valuesreduced_for_nanletbackward_addfkind~left~right~result=backward_add_onefkind~other:right~result>>-funleft'->backward_add_onefkind~other:left~result>>-funright'->`Value(left',right')letbackward_subfk~left~right~result=letright=negrightinbackward_addfk~left~right~result>>-:fun(left,right)->(left,negright)(* ------------------------------------------------------------------------
Exp Log Sqrt Pow Fmod
------------------------------------------------------------------------ *)let(>>:)tf=matchtwith|FRange.NaN->t|FRange.Itv(b,e,nan)->f~nanbeletapproxfprec~nanbe=letround=ifF.is_exactprecthenNearelseDowninletmin=froundprecbinletround=ifF.is_exactprecthenNearelseUpinletmax=froundpreceinFRange.inject~nanminmaxletexpprect=t>>:approxF.expprecletlog_auxlogprect=t>>:fun~nanbe->ifCmp.(lteneg_zero)thenFRange.nanelseletnan=nan||Cmp.(ltbneg_zero)inletb=Cmp.max(Cst.neg_zeroprec)binapproxlogprec~nanbeletlogprec=log_auxF.logprecletlog10prec=log_auxF.log10prec(* [sqrt_f] is the actual function computing the (exact) square root,
in single precision (sqrtf) or double precision (sqrt). *)letsqrtprect=t>>:fun~nanbe->ifCmp.(lt_ieeeeneg_zero)thenFRange.nanelseletnan,b=ifCmp.(ge_ieeebneg_zero)thennan,belsetrue,Cst.neg_zeroprecinapproxF.sqrtprec~nanbeletvalue_ifprecconditionf=ifconditionthen`Value(fprec)else`Bottom(* Returns the minimal or maximal (according to [min_or_max]) results of the
binary operation [op] applied to the bounds of the intervals [b1..e1] and
[b2..e2]. *)letextremummin_or_maxop(b1,e1)(b2,e2)=letextremum4abcd=min_or_maxa(min_or_maxb(min_or_maxcd))inextremum4(opb1b2)(opb1e2)(ope1b2)(ope1e2)(* Returns the minimum and maximum results of the binary operation [op] applied
to the bounds of the intervals [b1..e1] and [b2..e2]. *)letextremaop(b1,e1)(b2,e2)=leta=opb1b2andb=opb1e2andc=ope1b2andd=ope1e2inCmp.mina(Cmp.minb(Cmp.mincd)),Cmp.maxa(Cmp.maxb(Cmp.maxcd))(* Computes [pow_f] on a negative [bx; ex] interval (including infinites).
Processes by disjunction over even and odd integers enclosed within [by; ey].
[pow] is then monotonic on even integers (including zeros and infinities),
and on odd integers (except on infinities). *)letpow_negative_xpow_fprec(bx,exasx)(by,eyasy)=leteven,odd=split_by_parityprecyin(* Even integers [y] lead to positive results, while odd ones lead to negative
results. When [y] contains both even and odd integers, the minimum result
is in odd integers, and the maximum in even integers. *)letmin,max=matcheven,oddwith|None,None->Cst.pos_infinityprec,Cst.neg_infinityprec|Someeven,None->extremapow_fxeven|None,Someodd->extremapow_fxodd|Someeven,Someodd->extremumCmp.minpow_fxodd,extremumCmp.maxpow_fxeveninletnonint_y=contains_finite_nonintegerprecyin(* pow creates NaN when [x] is a negative non-zero finite value, and [y] a
non integer value. *)letnan=contains_strict_neg_finitex&&nonint_yin(* Special cases of neg_infinity and neg_zero for [x], that do not produce
a NaN on non integer [y], unlike strictly negative finite values [x]. *)letneg_nonint_y=nonint_y&&Cmp.(ltbyneg_zero)inletpos_nonint_y=nonint_y&&Cmp.(gteypos_zero)inletneg_infinity_x=Cmp.is_neg_infinitybxinletzero_x=Cmp.(equalexneg_zero)inBottom.join_listjoin[ifCmp.leminmaxthen`Value(FRange.injectminmax)else`Bottom;value_ifprecnan(fun_->FRange.nan);value_ifprec(neg_infinity_x&&neg_nonint_y)pos_zero;value_ifprec(neg_infinity_x&&pos_nonint_y)pos_infinity;value_ifprec(zero_x&&neg_nonint_y)pos_infinity;value_ifprec(zero_x&&pos_nonint_y)pos_zero](* Computes pow on a positive [bx; ex] interval (including infinites): the
function is continuous and monotonic. *)letpow_positive_xpow_f_precxy=letmin,max=extremapow_fxyinFRange.injectminmaxletpowprecxy=ifis_onex||is_a_zeroythenoneprecelse(x,y)>>%fun~nanitv_xitv_y->letpow_f=F.powNearprecin(* Split the x interval around zeros, as pow is discontinuous on zeros. *)letneg_x,pos_x=split_by_signprecitv_xinletpos_x_res=pos_x>>-:funx->pow_positive_xpow_fprecxitv_yinletneg_x_res=neg_x>>-funx->pow_negative_xpow_fprecxitv_yinletnan_res=ifnanthen`ValueFRange.nanelse`BottominBottom.non_bottom(Bottom.join_listjoin[pos_x_res;neg_x_res;nan_res])(* Is [fmod] continuous on positive intervals [b1..e1] and [b2..e2]?
This is the case if for all x, y in these intervals, the rounded quotient
[floor(x/y)] is constant, as [fmod x y = x - floor(x/y) * y] when [x] and [y]
are positive.
Also checks that [x/y < 2^53], otherwise truncation to an integer may return
an incorrect result. Note: to avoid issues with rounding, we conservatively
set the limit to 2^51 instead of 2^53 (and to 2^21 instead of 2^23 in single
precision). *)letis_fmod_continuousprec(b1,e1)(b2,e2)=(* Discontinuity of [fmod x y] on infinite [x] and on zero [y]. *)F.is_finitee1&&Cmp.(gt_ieeeb2pos_zero)&&letfour=F.of_floatNearprec4.inletmax_i=F.divNearprec(Cst.max_precise_integerprec)fourinletf1=F.floor(F.divZeroprecb1e2)inletf2=F.floor(F.divZeroprece1b2)inCmp.equalf1f2&&Cmp.lef1max_i&&F.is_exactprec(* Forward semantics of fmod on positive intervals. [x] must contain finite
values, and [y] must contain non-zero values, in which case finite values
are produced. This function does not check the creation of NaN. *)letpositive_fmodprec(b1,e1asx)(b2,e2asy)=letfmod=F.fmodNearprecin(* Singleton case. [x] cannot be infinite, and [y] cannot be zero. *)ifCmp.equalb1e1&&Cmp.equalb2e2thenletc=fmodb1b2inc,c(* If all values of x are smaller than all values of [y], [x] is unchanged. *)elseifCmp.lt_ieeee1b2thenx(* If fmod is continuous on the intervals [x] and [y], it is also monotonic,
and the bounds of the result are the remainders of the bounds. *)elseifis_fmod_continuousprecxythenfmodb1e2,fmode1b2(* Otherwise, fmod always satisfies 0 <= [fmod x y] <= x and [fmod x y] < y. *)elseletmax=Cmp.mine1(F.prev_floatprece2)in(Cst.pos_zeroprec,max)letfmodprecxy=(* [fmod x (-y)] = [fmod x y], so use only positive [y]. *)lety=absprecyin(x,y)>>%fun~nan(b1,e1)(b2,e2)->(* If [x] is an infinite singleton, or if [y] contains only zero, only NaN
can be created. *)if(Cmp.equalb1e1&&F.is_infiniteb1)||(Cmp.equalb2e2&&Cmp.is_a_zerob2)thenFRange.nanelseletnan=nan||contains_infinityx||contains_a_zeroyinletpositive_fmodx=letb,e=positive_fmodprecx(b2,e2)inFRange.inject~nanbein(* Process by disjunction on the sign of [x], and join the results for
negative x and for positive x. *)letneg_x,pos_x=split_by_signprec(b1,e1)inletneg_itv(b,e)=F.nege,F.negbinletneg_r=neg_x>>-:neg_itv>>-:positive_fmod>>-:neginletpos_r=pos_x>>-:positive_fmodinBottom.non_bottom(Bottom.joinjoinneg_rpos_r)(* ------------------------------------------------------------------------
Trigonometry
------------------------------------------------------------------------ *)(* It is wrong to use m_pi as the local minimum as was previously done,
because:
A = 3.14159265358979312 and A < m_pi, cos A = 1. and cos m_pi = 0.999
Moreover it is not due to the imprecision in the value of pi:
A < pred m_pi in simple.
So we use a quarter of the interval [-pi:pi] to be safe. (But still,
nothing proves that cos or sin are monotonic on those ranges.) *)letm_pi=3.1415929794311523(* single-precision *)letcos_sin_security=F.of_floatNearFloat_sig.Single(m_pi/.4.)letcos_sinopprect=t>>:fun~nanbe->(* Special case when at least a bound is infinite. *)ifF.is_infiniteb||F.is_infiniteethenifCmp.equalbethenFRange.nanelseminus_one_oneprec~nan:true(* [b] and [e] are finite. Precise case for a singleton. *)elseifCmp.equalbethenletc=opNearprecbinFRange.inject~nanccelseifCmp.le_ieeeb(F.negcos_sin_security)||Cmp.ge_ieeeecos_sin_securitythenminus_one_oneprec~nanelseletxl=ifCmp.(ltbpos_zero)&&Cmp.(ltpos_zeroe)then[b;e;Cst.pos_zeroprec]else[b;e]inletl=List.map(opNearprec)xlinletmin_f=List.fold_leftCmp.min(Cst.pos_infinityprec)linletmax_f=List.fold_leftCmp.max(Cst.neg_infinityprec)linFRange.inject~nanmin_fmax_fletcosprec=cos_sinF.cosprecletsinprec=cos_sinF.sinprecletatan2precxy=(x,y)>>%fun~nan(b1,e1)(b2,e2)->letop=F.atan2NearprecinifCmp.equalb1e1&&Cmp.equalb2e2thenletc=opb1b2inFRange.inject~nanccelse(* Unless y ([b1,e1]) crosses the x-axis, atan2 is continuous,
and its minimum/maximum are at the ends of the intervals of x and y.
Otherwise, the result is [-pi,+pi]. *)ifCmp.(ltb1pos_zero)&&Cmp.(gte1neg_zero)thenminus_pi_piprec~nanelseleta1,a2,a3,a4=opb1b2,opb1e2,ope1b2,ope1e2inletb=Cmp.mina1(Cmp.mina2(Cmp.mina3a4))inlete=Cmp.maxa1(Cmp.maxa2(Cmp.maxa3a4))inmatchprecwith|Float_sig.Single->(* Rounding of atan2f in single-precision may go against monotony and
reach the next (previous) float after (before) the bounds. *)FRange.inject~nan(Cmp.prev_float_ieeeprecb)(Cmp.next_float_ieeeprece)|_->FRange.inject~nanbe(* ------------------------------------------------------------------------
Casts
------------------------------------------------------------------------ *)letforward_cast~dst=function|FRange.NaN->nan|FRange.Itv(b,e,nan)->letround=F.round_to_precisionNeardstininject~nan(roundb)(rounde)(* This function must make sure to return a result with float 32 bounds *)letbackward_cast~src=function|FRange.NaN->`Valuenan|FRange.Itv(b,e,nan)->FRange.inject_after_tightensrc~nanbeletcast_int_to_floatprecminmax=letmin=matchminwith|None->Cst.neg_infinityprec|Somev->letround=ifF.is_exactprecthenNearelseDowninF.of_floatroundprec(Integer.to_floatv)inletmax=matchmaxwith|None->Cst.neg_infinityprec|Somev->letround=ifF.is_exactprecthenNearelseUpinF.of_floatroundprec(Integer.to_floatv)inFRange.injectminmax(* Bitwise reinterpretation of a floating-point value into consecutive
ranges of integer. This operation is exact in terms of
concretization. 'Parametric' in the number of bits. *)letbits_of_float_list~prec~bits_of_float~max_int=letneg_infinity=Cst.neg_infinityprecandpos_infinity=Cst.pos_infinityprecinletitvs_nan=letsmallest_neg_nan=Integer.succ(bits_of_floatneg_infinity)inletbiggest_neg_nan=Integer.minus_oneinletsmallest_pos_nan=Integer.succ(bits_of_floatpos_infinity)inletbiggest_pos_nan=max_intin[(smallest_neg_nan,biggest_neg_nan);(smallest_pos_nan,biggest_pos_nan)]infunction|FRange.NaN->itvs_nan|FRange.Itv(b,e,nan)->letnan=ifnanthenitvs_nanelse[]inletneg,pos=split_by_signprec(b,e)inletneg=neg>>-:fun(b,e)->bits_of_floate,bits_of_floatbinletpos=pos>>-:fun(b,e)->bits_of_floatb,bits_of_floateinBottom.add_to_listpos(Bottom.add_to_listnegnan)letbits_of_float64_list=letbits_of_floatf=Integer.of_int64(Int64.bits_of_float(F.to_floatf))inletmax_int=Integer.of_int64Int64.max_intinbits_of_float_list~prec:Double~bits_of_float~max_intletbits_of_float32_list=letbits_of_floatf=Integer.of_int32(Int32.bits_of_float(F.to_floatf))inletmax_int=Integer.of_int32Int32.max_intinbits_of_float_list~prec:Single~bits_of_float~max_int(* ------------------------------------------------------------------------
Subdivision
------------------------------------------------------------------------ *)(* [avg] and [split] implement two different strategies for cutting a
floating-point interval in half: [avg] computes the mathematical average of
the two bounds, while [split] balances the number of representable values
of the given precision in each resulting intervals. *)(* Computes the average between two ocaml doubles. *)letavgxy=letfx=F.to_floatxandfy=F.to_floatyinifF.is_negativex&&F.is_negativeythenfy+.(fx-.fy)/.2.else(fx+.fy)/.2.(* assumption: [0. <= x <= y]. returns the median of the range [x..y]
in number of double values. *)letsplit_positiveprecxy=letix=Int64.bits_of_float(F.to_floatx)inletiy=Int64.bits_of_float(F.to_floaty)inletf=Int64.(float_of_bits(addix(div(subiyix)2L)))inF.of_floatNearprecf(* assumption: [x <= y] *)let_splitprecxy=matchF.is_negativex,F.is_negativeywith|false,false->split_positiveprecxy|true,true->F.neg(split_positiveprec(F.negx)(F.negy))|true,false->Cst.neg_zeroprec|false,true->assertfalseexceptionCan_not_subdiv=Abstract_interp.Can_not_subdivletsubdivideprect=assert(prec=Single||prec=Double);(* See Value/Value#105 *)matchtwith|FRange.NaN->raiseCan_not_subdiv|FRange.Itv(b,e,nan)->ifnanthenFRange.inject~nan:falsebe,FRange.nanelseifCmp.equalbethenraiseCan_not_subdivelseletmidpoint,smidpoint=(* Infinities are interesting points to consider specially. *)ifCmp.is_neg_infinitybthenCst.neg_infinityprec,F.next_floatprec(Cst.neg_infinityprec)elseifCmp.is_pos_infinityethenF.prev_floatprec(Cst.pos_infinityprec),Cst.pos_infinityprecelseifCmp.equal(F.next_floatprecb)ethenb,eelseletmidpoint=avgbeinletmidpoint=F.of_floatDownprecmidpointinletsmidpoint=ifF.is_exactprecthenF.next_floatprecmidpointelsemidpointinifCmp.lesmidpointethenmidpoint,smidpointelsemidpoint,einifCmp.lesmidpointb||Cmp.leemidpointthenraiseCan_not_subdiv;leti1=FRange.inject~nanbmidpointinassert(is_includedi1t);leti2=FRange.inject~nansmidpointeinassert(is_includedi2t);i1,i2end