123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* This program is free software: 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, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* This program 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. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* along with this program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(**
FloatItv - Floating-point interval arithmetics with rounding.
We rely on C code to provide functions with correct rounding
(rounding direction and rounding precision).
*)openBotmoduleF=FloatmoduleB=IntBoundmoduleII=IntItv(** {2 Types} *)typet={mutablelo:float;(** lower bound *)mutableup:float;(** upper bound *)}(**
The type of non-empty intervals: a lower bound and an upper bound.
The bounds can be -∞ and +∞.
In particular, we can have [-∞,-∞] and [+∞,+∞] (useful to model sets
of floating-point numbers).
We must have lo ≤ up.
The bounds shall not be NaN.
*)typet_with_bot=twith_bot(** The type of possibly empty intervals. *)letis_valid(a:t):bool=a.lo<=a.up&¬(F.is_nana.lo||F.is_nana.up)(** {2 Constructors} *)letmk(lo:float)(up:float):t={lo;up;}letzero:t=mk0.0.letone:t=mk1.1.lettwo:t=mk2.2.letmone:t=mk(-1.)(-1.)letzero_one:t=mk0.1.letmone_zero:t=mk(-1.)0.letmone_one:t=mk(-1.)1.letmhalf_half:t=mk(-0.5)0.5letzero_inf:t=mk0.infinityletminf_zero:t=mkneg_infinity0.letminf_inf:t=mkneg_infinityinfinity(** Useful intervals *)letof_float(lo:float)(up:float):t=letlo=ifF.is_nanlothenneg_infinityelseloandup=ifF.is_nanuptheninfinityelseupiniflo>uptheninvalid_arg(Printf.sprintf"FloatItv.of_float: invalid bound [%g,%g]"loup)else{lo;up;}(** Constructs a non-empty interval. We must have lo ≤ up, or an exception is raised.
NaN bounds are transformed into infinities.
*)letof_float_bot(lo:float)(up:float):t_with_bot=letlo=ifF.is_nanlothenneg_infinityelseloandup=ifF.is_nanuptheninfinityelseupiniflo<=upthenNb{lo;up;}elseBOT(** Constructs a possibly empty interval (no rounding).
NaN bounds are transformed into infinities.
*)lethull(a:float)(b:float):t=ifF.is_nana||F.is_nanbthenminf_infelsemk(minab)(maxab)(** Constructs the smallest interval containing a and b. *)letcst(c:float):t=ifF.is_nancthenminf_infelsemkcc(** Singleton interval. *)(** {2 Predicates} *)letequal(a:t)(b:t):bool=a=bletequal_bot:t_with_bot->t_with_bot->bool=bot_equalequalletincluded(a:t)(b:t):bool=a.lo>=b.lo&&a.up<=b.up(** Set ordering. = also works to compare for equality. *)letincluded_bot:t_with_bot->t_with_bot->bool=bot_includedincludedletintersect(a:t)(b:t):bool=a.lo<=b.up&&b.lo<=a.up(** Whether the intervals have an non-empty intersection. *)letintersect_bot:t_with_bot->t_with_bot->bool=bot_dfl2falseintersectletcontains(x:float)(a:t)=a.lo<=x&&a.up>=x(** Whether the interval contains a certain value. *)letcompare(a:t)(b:t):int=ifa.lo=b.lothencomparea.upb.upelsecomparea.lob.lo(**
A total ordering of intervals (lexical ordering) returning -1, 0, or 1.
Can be used as compare for sets, maps, etc.
(The hypothesis that bounds cannot be NaN is important to make the order total.
*)letcompare_bot(x:twith_bot)(y:twith_bot):int=Bot.bot_comparecomparexy(** Total ordering on possibly empty intervals. *)letis_zero(a:t):bool=equalazeroletis_positive(a:t):bool=a.lo>=0.letis_negative(a:t):bool=a.up<=0.letis_positive_strict(a:t):bool=a.lo>0.letis_negative_strict(a:t):bool=a.up<0.letis_nonzero(a:t):bool=a.lo>0.||a.up<0.(** Interval sign. *)letcontains_positive(a:t):bool=a.up>=0.letcontains_negative(a:t):bool=a.lo<=0.letcontains_positive_strict(a:t):bool=a.up>0.letcontains_negative_strict(a:t):bool=a.lo<0.letcontains_zero(a:t):bool=a.lo<=0.&&a.up>=0.letcontains_nonzero(a:t):bool=a.lo<>0.||a.up<>0.(** Whether the interval contains an element of the specified sign. *)letis_singleton(a:t):bool=a.lo=a.up(** Whether the interval contains a single element. *)letis_bounded(a:t):bool=a.lo>neg_infinity&&a.up<infinity(** Whether the interval has finite bounds. *)letis_in_range(a:t)(lo:float)(up:float)=includeda{lo;up;}(** Whether the interval is included in the range [lo,up]. *)letis_log_eq(ab:t)(ab':t):bool=intersectabab'letis_log_leq({lo=a;up=b}:t)({lo=a';up=b'}:t):bool=F.leqab'letis_log_geq({lo=a;up=b}:t)({lo=a';up=b'}:t):bool=F.geqba'letis_log_lt({lo=a;up=b}:t)({lo=a';up=b'}:t):bool=F.ltab'letis_log_gt({lo=a;up=b}:t)({lo=a';up=b'}:t):bool=F.gtba'letis_log_neq(ab:t)(ab':t):bool=not(equalabab'&&is_singletonab)(** C comparison tests. Returns true if the test may succeed, false if it cannot. *)(** {2 Printing} *)typeprint_format=F.print_formatletdfl_fmt=F.dfl_fmtletto_string(fmt:print_format)(x:t):string="["^(F.to_stringfmtx.lo)^","^(F.to_stringfmtx.up)^"]"letprintfmtch(x:t)=output_stringch(to_stringfmtx)letfprintfmtch(x:t)=Format.pp_print_stringch(to_stringfmtx)letbprintfmtch(x:t)=Buffer.add_stringch(to_stringfmtx)letto_string_botfmt=bot_to_string(to_stringfmt)letprint_botfmt=bot_print(printfmt)letfprint_botfmt=bot_fprint(fprintfmt)letbprint_botfmt=bot_bprint(bprintfmt)(** {2 Operations without rounding} *)letneg(t:t):t=of_float(-.t.up)(-.t.lo)(** Negation. *)letabs(t:t):t=ift.lo<=0.thenift.up<=0.thennegtelseof_float0.(max(-.t.lo)t.up)elset(** Absolute value. *)letfmod(x:t)(y:t):t_with_bot=(* x % y = x % |y| *)lety=absyinify=zerothenBOTelse(* case [a,b] % {0} ⟹ ⊥ *)ifx.lo>-.y.lo&&x.up<y.lothen(* case x ⊆ [-min y, min y] ⟹ identity *)Nbxelseify.lo=y.up&&F.is_finitey.lo&&F.is_finitex.lo&&F.is_finitex.up&&F.Double.round_int_zero(F.Double.div_zerox.loy.lo)=F.Double.round_int_zero(F.Double.div_zerox.upy.lo)then(* case x % {z} and x ⊆ [zk,z(k+1)] *)Nb(of_float(F.fmodx.loy.lo)(F.fmodx.upy.lo))elseifx.lo>=0.then(* case x % y positive *)Nb(of_float0.y.up)elseifx.up<=0.then(* case x % y negative *)Nb(of_float(-.y.up)0.)else(* general case *)Nb(of_float(-.y.up)y.up)(** Remainder (fmod). *)letto_z(r:t):Z.t*Z.t=ifnot(F.is_finiter.lo&&F.is_finiter.up)theninvalid_arg"Inifnites or NaN in FloatItv.to_z";Z.of_floatr.lo,Z.of_floatr.up(** Conversion to integer (using truncation). *)letto_int_itv(r:t):II.twith_bot=ifF.is_nanr.lo||F.is_nanr.upthenNbII.minf_infelseifr.lo=infinity||r.up=neg_infinitythenBOTelseletx=ifr.lo=neg_infinitythenB.MINFelseB.Finite(Z.of_floatr.lo)andy=ifr.up=infinitythenB.PINFelseB.Finite(Z.of_floatr.up)inNb(x,y)letjoin(a:t)(b:t):t=of_float(mina.lob.lo)(maxa.upb.up)(** Join of non-empty intervals. *)letjoin_bot(a:t_with_bot)(b:t_with_bot):t_with_bot=bot_neutral2joinab(** Join of possibly empty intervals. *)letjoin_list(l:tlist):t_with_bot=List.fold_left(funab->join_bota(Nbb))BOTl(** Join of a list of (non-empty) intervals. *)letmeet(a:t)(b:t):t_with_bot=of_float_bot(maxa.lob.lo)(mina.upb.up)(** Intersection of non-emtpty intervals (possibly empty) *)letmeet_bot(a:t_with_bot)(b:t_with_bot):t_with_bot=bot_absorb2meetab(** Intersection of possibly empty intervals. *)letmeet_list(l:tlist):t_with_bot=List.fold_left(funab->meet_bota(Nbb))(Nbminf_inf)l(** Meet of a list of (non-empty) intervals. *)letpositive(a:t):t_with_bot=meetazero_infletnegative(a:t):t_with_bot=meetaminf_zero(** Positive and negative part. *)letmeet_zero(a:t):t_with_bot=meetazero(** Intersects with {0}. *)letwiden(a:t)(a':t):t=of_float(ifF.lta'.loa.lothenneg_infinityelsea.lo)(ifF.gta'.upa.uptheninfinityelsea.up)(** Basic widening: put unstable bounds to infinity. *)letwiden_bot(a:t_with_bot)(b:t_with_bot):t_with_bot=bot_neutral2widenabletbwd_default_unary(a:t)(r:t):t_with_bot=Nba(** Fallback for backward unary operators *)letbwd_default_binary(a:t)(b:t)(r:t):(t*t)with_bot=Nb(a,b)(** Fallback for backward binary operators *)letbwd_neg(a:t)(r:t):t_with_bot=meeta(negr)(** Backward negation. *)letbwd_abs(a:t)(r:t):t_with_bot=join_bot(meetar)(meeta(negr))(** Backward absolute value. *)letbwd_fmod(x:t)(y:t)(r:t):(t*t)with_bot=letyy=absyinifx.lo>-.yy.lo&&x.up<yy.lothen(* case x ⊆ [-min |y|, min |y|] ⟹ fmod is the identity on x *)bot_merge2(meetxr)(Nby)else(* default: no refinement *)Nb(x,y)(** Backward remainder (fmod). *)(** {2 Rounding-dependent functions} *)(**
Interval operations support six rounding modes.
The first four correspond to rounding both bounds in the same direction:
to nearest, upwards, downards, or to zero.
To this, we add outer rounding (lower bound downwards and upper bound
upwards) and inner rounding (lower bound upwards and upper bound
downwards).
Rounding can be performed for single-precision or double-precision.
Outer interval arithmetic can model soundly real arithmetic. In this
case, infinities model unbounded intervals.
Directed roundings and outer arithmetics can model soundly float
arithmetic. In this case, infinite bounds signal the precence of
infinite float values.
Directed roundings can produce [-∞,-∞] or [+∞,+∞] (denoting a set
of floats reduced to an infinite float).
Inner rounding can return empty intervals. Divisions and square roots
can return empty intervals for any rounding mode.
We do not track NaN.
NaN bounds, as in [-∞,-∞] + [+∞,+∞], are transformed into infinities.
*)(* binding to (internal) C functions *)(* The C functions are designed to avoid allocation. We pass them the struct
that will be set to the result.
We do not export them directly, but we wrap in some OCaml code to give
them a functional flavor.
*)externaladd_dbl_itv_near:t->t->t->unit="ml_add_dbl_itv_near"externaladd_dbl_itv_up:t->t->t->unit="ml_add_dbl_itv_up"externaladd_dbl_itv_down:t->t->t->unit="ml_add_dbl_itv_down"externaladd_dbl_itv_zero:t->t->t->unit="ml_add_dbl_itv_zero"externaladd_dbl_itv_outer:t->t->t->unit="ml_add_dbl_itv_outer"externaladd_dbl_itv_inner:t->t->t->unit="ml_add_dbl_itv_inner"externaladd_sgl_itv_near:t->t->t->unit="ml_add_sgl_itv_near"externaladd_sgl_itv_up:t->t->t->unit="ml_add_sgl_itv_up"externaladd_sgl_itv_down:t->t->t->unit="ml_add_sgl_itv_down"externaladd_sgl_itv_zero:t->t->t->unit="ml_add_sgl_itv_zero"externaladd_sgl_itv_outer:t->t->t->unit="ml_add_sgl_itv_outer"externaladd_sgl_itv_inner:t->t->t->unit="ml_add_sgl_itv_inner"externalsub_dbl_itv_near:t->t->t->unit="ml_sub_dbl_itv_near"externalsub_dbl_itv_up:t->t->t->unit="ml_sub_dbl_itv_up"externalsub_dbl_itv_down:t->t->t->unit="ml_sub_dbl_itv_down"externalsub_dbl_itv_zero:t->t->t->unit="ml_sub_dbl_itv_zero"externalsub_dbl_itv_outer:t->t->t->unit="ml_sub_dbl_itv_outer"externalsub_dbl_itv_inner:t->t->t->unit="ml_sub_dbl_itv_inner"externalsub_sgl_itv_near:t->t->t->unit="ml_sub_sgl_itv_near"externalsub_sgl_itv_up:t->t->t->unit="ml_sub_sgl_itv_up"externalsub_sgl_itv_down:t->t->t->unit="ml_sub_sgl_itv_down"externalsub_sgl_itv_zero:t->t->t->unit="ml_sub_sgl_itv_zero"externalsub_sgl_itv_outer:t->t->t->unit="ml_sub_sgl_itv_outer"externalsub_sgl_itv_inner:t->t->t->unit="ml_sub_sgl_itv_inner"externalmul_dbl_itv_near:t->t->t->unit="ml_mul_dbl_itv_near"externalmul_dbl_itv_up:t->t->t->unit="ml_mul_dbl_itv_up"externalmul_dbl_itv_down:t->t->t->unit="ml_mul_dbl_itv_down"externalmul_dbl_itv_zero:t->t->t->unit="ml_mul_dbl_itv_zero"externalmul_dbl_itv_outer:t->t->t->unit="ml_mul_dbl_itv_outer"externalmul_dbl_itv_inner:t->t->t->unit="ml_mul_dbl_itv_inner"externalmul_sgl_itv_near:t->t->t->unit="ml_mul_sgl_itv_near"externalmul_sgl_itv_up:t->t->t->unit="ml_mul_sgl_itv_up"externalmul_sgl_itv_down:t->t->t->unit="ml_mul_sgl_itv_down"externalmul_sgl_itv_zero:t->t->t->unit="ml_mul_sgl_itv_zero"externalmul_sgl_itv_outer:t->t->t->unit="ml_mul_sgl_itv_outer"externalmul_sgl_itv_inner:t->t->t->unit="ml_mul_sgl_itv_inner"externaldivpos_dbl_itv_near:t->t->t->unit="ml_divpos_dbl_itv_near"externaldivpos_dbl_itv_up:t->t->t->unit="ml_divpos_dbl_itv_up"externaldivpos_dbl_itv_down:t->t->t->unit="ml_divpos_dbl_itv_down"externaldivpos_dbl_itv_zero:t->t->t->unit="ml_divpos_dbl_itv_zero"externaldivpos_dbl_itv_outer:t->t->t->unit="ml_divpos_dbl_itv_outer"externaldivpos_dbl_itv_inner:t->t->t->unit="ml_divpos_dbl_itv_inner"externaldivpos_sgl_itv_near:t->t->t->unit="ml_divpos_sgl_itv_near"externaldivpos_sgl_itv_up:t->t->t->unit="ml_divpos_sgl_itv_up"externaldivpos_sgl_itv_down:t->t->t->unit="ml_divpos_sgl_itv_down"externaldivpos_sgl_itv_zero:t->t->t->unit="ml_divpos_sgl_itv_zero"externaldivpos_sgl_itv_outer:t->t->t->unit="ml_divpos_sgl_itv_outer"externaldivpos_sgl_itv_inner:t->t->t->unit="ml_divpos_sgl_itv_inner"(* internal utilities *)letmkop()={lo=0.;up=0.;}letwrap_op1f=funa->letr=mkop()infar;rletwrap_op2f=funab->letr=mkop()infabr;r(* wrapper helpers from imperative C to functional OCaml *)letwrap_op1_botfa=letr=wrap_op1fainifr.lo<=r.upthenNbrelseBOTletwrap_op2_botfab=letr=wrap_op2fabinifr.lo<=r.upthenNbrelseBOTletwrap_sqrtfga=ifa.up<0.thenBOTelseof_float_bot(f(max0.a.lo))(ga.up)(* wrapper for sqrt *)letwrap_div_unmergeddab=letl1=ifb.up>0.then[da{bwithlo=ifF.signb.lo>0thenb.loelse0.;}]else[]andl2=ifb.lo<0.then[da{bwithup=ifF.signb.up<0thenb.upelse-.0.;}]else[]inl1@l2(* wrapper for division;
we split into a positive divisor interval and a negative divisor interval;
we have to be extra careful of the sign of zeros when splitting (to get the correct infinity);
returns a list of intervals to keep precision;
*)letwrap_div_unmerged_botdab=list_remove_bot(wrap_div_unmergeddab)letwrap_divdab=join_list(wrap_div_unmergeddab)(* wrapper for division; returns a single (possibly empty) interval *)letwrap_div_botdab=join_list(wrap_div_unmerged_botdab)moduleDouble=structmoduleFF=F.Double(** {2 Arithmetic} *)letadd_near:t->t->t=wrap_op2add_dbl_itv_nearletadd_up:t->t->t=wrap_op2add_dbl_itv_upletadd_down:t->t->t=wrap_op2add_dbl_itv_downletadd_zero:t->t->t=wrap_op2add_dbl_itv_zeroletadd_outer:t->t->t=wrap_op2add_dbl_itv_outerletadd_inner:t->t->t_with_bot=wrap_op2_botadd_dbl_itv_inner(** Addition. *)letsub_near:t->t->t=wrap_op2sub_dbl_itv_nearletsub_up:t->t->t=wrap_op2sub_dbl_itv_upletsub_down:t->t->t=wrap_op2sub_dbl_itv_downletsub_zero:t->t->t=wrap_op2sub_dbl_itv_zeroletsub_outer:t->t->t=wrap_op2sub_dbl_itv_outerletsub_inner:t->t->t_with_bot=wrap_op2_botsub_dbl_itv_inner(** Subtraction. *)letmul_near:t->t->t=wrap_op2mul_dbl_itv_nearletmul_up:t->t->t=wrap_op2mul_dbl_itv_upletmul_down:t->t->t=wrap_op2mul_dbl_itv_downletmul_zero:t->t->t=wrap_op2mul_dbl_itv_zeroletmul_outer:t->t->t=wrap_op2mul_dbl_itv_outerletmul_inner:t->t->t_with_bot=wrap_op2_botmul_dbl_itv_inner(** Multiplication. *)letdivpos_near:t->t->t=wrap_op2divpos_dbl_itv_nearletdivpos_up:t->t->t=wrap_op2divpos_dbl_itv_upletdivpos_down:t->t->t=wrap_op2divpos_dbl_itv_downletdivpos_zero:t->t->t=wrap_op2divpos_dbl_itv_zeroletdivpos_outer:t->t->t=wrap_op2divpos_dbl_itv_outerletdivpos_inner:t->t->t_with_bot=wrap_op2_botdivpos_dbl_itv_inner(* Division by a divisor of constant sign. *)letdiv_unmerged_near:t->t->tlist=wrap_div_unmergeddivpos_nearletdiv_unmerged_up:t->t->tlist=wrap_div_unmergeddivpos_upletdiv_unmerged_down:t->t->tlist=wrap_div_unmergeddivpos_downletdiv_unmerged_zero:t->t->tlist=wrap_div_unmergeddivpos_zeroletdiv_unmerged_outer:t->t->tlist=wrap_div_unmergeddivpos_outerletdiv_unmerged_inner:t->t->tlist=wrap_div_unmerged_botdivpos_inner(** Division. Returns a list of 0, 1, or 2 intervals to remain precise. *)letdiv_near:t->t->t_with_bot=wrap_divdivpos_nearletdiv_up:t->t->t_with_bot=wrap_divdivpos_upletdiv_down:t->t->t_with_bot=wrap_divdivpos_downletdiv_zero:t->t->t_with_bot=wrap_divdivpos_zeroletdiv_outer:t->t->t_with_bot=wrap_divdivpos_outerletdiv_inner:t->t->t_with_bot=wrap_div_botdivpos_inner(** Division. Returns a single interval. *)letsquare_near(a:t):t=letaa=absainmul_nearaaaaletsquare_up(a:t):t=letaa=absainmul_upaaaaletsquare_down(a:t):t=letaa=absainmul_downaaaaletsquare_zero(a:t):t=letaa=absainmul_zeroaaaaletsquare_outer(a:t):t=letaa=absainmul_outeraaaaletsquare_inner(a:t):t_with_bot=letaa=absainmul_inneraaaa(** Square. *)letsqrt_near:t->t_with_bot=wrap_sqrtFF.sqrt_nearFF.sqrt_nearletsqrt_up:t->t_with_bot=wrap_sqrtFF.sqrt_upFF.sqrt_upletsqrt_down:t->t_with_bot=wrap_sqrtFF.sqrt_downFF.sqrt_downletsqrt_zero:t->t_with_bot=wrap_sqrtFF.sqrt_zeroFF.sqrt_zeroletsqrt_outer:t->t_with_bot=wrap_sqrtFF.sqrt_downFF.sqrt_upletsqrt_inner:t->t_with_bot=wrap_sqrtFF.sqrt_upFF.sqrt_down(** Square root. Returns the square root of the positive part, possibly ⊥. *)letround_int_near(a:t):t=of_float(FF.round_int_neara.lo)(FF.round_int_neara.up)letround_int_up(a:t):t=of_float(FF.round_int_upa.lo)(FF.round_int_upa.up)letround_int_down(a:t):t=of_float(FF.round_int_downa.lo)(FF.round_int_downa.up)letround_int_zero(a:t):t=of_float(FF.round_int_zeroa.lo)(FF.round_int_zeroa.up)letround_int_outer(a:t):t=of_float(FF.round_int_downa.lo)(FF.round_int_upa.up)letround_int_inner(a:t):t_with_bot=of_float_bot(FF.round_int_upa.lo)(FF.round_int_downa.up)(** Round to integer. *)letunround_int_near(a:t):t=add_outeramhalf_halfletunround_int_up(a:t):t=sub_outerazero_oneletunround_int_down(a:t):t=add_outerazero_oneletunround_int_zero(a:t):t=of_float(ifa.lo<=0.thenFF.sub_downa.lo1.elsea.lo)(ifa.up>=0.thenFF.add_upa.up1.elsea.up)letunround_int_any(a:t):t=sub_outeramone_one(** Values that, after rounding to integer in the specified direction, may be in the argument interval.
Useful for backward operators.
*)letunround_near(a:t):t=of_float(FF.preda.lo)(FF.succa.up)letunround_up(a:t):t=of_float(FF.preda.lo)(a.up)letunround_down(a:t):t=of_float(a.lo)(FF.succa.up)letunround_zero(a:t):t=of_float(ifa.lo<=0.thenFF.preda.loelsea.lo)(ifa.up>=0.thenFF.succa.upelsea.up)letunround_any(a:t):t=unround_neara(** Values that, after rounding to float, may be in the argument interval.
Useful for backward operators.
*)letof_int_near(lo:int)(up:int):t=of_float(FF.of_int_nearlo)(FF.of_int_nearup)letof_int_up(lo:int)(up:int):t=of_float(FF.of_int_uplo)(FF.of_int_upup)letof_int_down(lo:int)(up:int):t=of_float(FF.of_int_downlo)(FF.of_int_downup)letof_int_zero(lo:int)(up:int):t=of_float(FF.of_int_zerolo)(FF.of_int_zeroup)letof_int_outer(lo:int)(up:int):t=of_float(FF.of_int_downlo)(FF.of_int_upup)letof_int_inner(lo:int)(up:int):t_with_bot=of_float_bot(FF.of_int_uplo)(FF.of_int_downup)(** Conversion from int. *)letof_int64_near(lo:int64)(up:int64):t=of_float(FF.of_int64_nearlo)(FF.of_int64_nearup)letof_int64_up(lo:int64)(up:int64):t=of_float(FF.of_int64_uplo)(FF.of_int64_upup)letof_int64_down(lo:int64)(up:int64):t=of_float(FF.of_int64_downlo)(FF.of_int64_downup)letof_int64_zero(lo:int64)(up:int64):t=of_float(FF.of_int64_zerolo)(FF.of_int64_zeroup)letof_int64_outer(lo:int64)(up:int64):t=of_float(FF.of_int64_downlo)(FF.of_int64_upup)letof_int64_inner(lo:int64)(up:int64):t_with_bot=of_float_bot(FF.of_int64_uplo)(FF.of_int64_downup)(** Conversion from int64. *)letof_z_near(lo:Z.t)(up:Z.t):t=of_float(FF.of_z_nearlo)(FF.of_z_nearup)letof_z_up(lo:Z.t)(up:Z.t):t=of_float(FF.of_z_uplo)(FF.of_z_upup)letof_z_down(lo:Z.t)(up:Z.t):t=of_float(FF.of_z_downlo)(FF.of_z_downup)letof_z_zero(lo:Z.t)(up:Z.t):t=of_float(FF.of_z_zerolo)(FF.of_z_zeroup)letof_z_outer(lo:Z.t)(up:Z.t):t=of_float(FF.of_z_downlo)(FF.of_z_upup)letof_z_inner(lo:Z.t)(up:Z.t):t_with_bot=of_float_bot(FF.of_z_uplo)(FF.of_z_downup)(** Conversion from Z.t. *)(** {2 Filters} *)(** Given two interval aruments, return the arguments assuming that the predicate holds.
*)letfilter_leq(a:t)(b:t):(t*t)with_bot=bot_merge2(of_float_bota.lo(mina.upb.up))(of_float_bot(maxa.lob.lo)b.up)letfilter_geq(a:t)(b:t):(t*t)with_bot=bot_merge2(of_float_bot(maxa.lob.lo)a.up)(of_float_botb.lo(mina.upb.up))letfilter_lt(a:t)(b:t):(t*t)with_bot=bot_merge2(of_float_bota.lo(mina.up(FF.predb.up)))(of_float_bot(max(FF.succa.lo)b.lo)b.up)letfilter_gt(a:t)(b:t):(t*t)with_bot=bot_merge2(of_float_bot(maxa.lo(FF.succb.lo))a.up)(of_float_botb.lo(min(FF.preda.up)b.up))letfilter_eq(a:t)(b:t):(t*t)with_bot=matchmeetabwithBOT->BOT|Nbx->Nb(x,x)letfilter_neq(a:t)(b:t):(t*t)with_bot=matcha.lo=a.up,b.lo=b.upwith|true,truewhena.lo=b.lo->BOT|true,falsewhena.lo=b.lo->bot_merge2(Nba)(of_float_bot(FF.succb.lo)b.up)|true,falsewhena.up=b.up->bot_merge2(Nba)(of_float_botb.lo(FF.predb.up))|false,truewhena.lo=b.lo->bot_merge2(of_float_bot(FF.succa.lo)a.up)(Nbb)|false,truewhena.up=b.up->bot_merge2(of_float_bota.lo(FF.preda.up))(Nbb)|_->Nb(a,b)(** {2 Backward operations} *)(** Given one or two interval argument(s) and a result interval, return the
argument(s) assuming the result in the operation is in the given result.
*)letbwd_add(a:t)(b:t)(r:t):(t*t)with_bot=(* r = round(a + b) ⇒ a = unround(r) - b ∧ b = unround(r) - a *)bot_merge2(meeta(sub_outerrb))(meetb(sub_outerra))letbwd_add_nearabr=bwd_addab(unround_nearr)letbwd_add_upabr=bwd_addab(unround_upr)letbwd_add_downabr=bwd_addab(unround_downr)letbwd_add_zeroabr=bwd_addab(unround_zeror)letbwd_add_anyabr=bwd_addab(unround_anyr)letbwd_add_noroundabr=bwd_addabr(** Backward addition. *)letbwd_sub(a:t)(b:t)(r:t):(t*t)with_bot=(* r = round(a - b) ⇒ a = b + unround(r) ∧ b = a - unround(r) *)bot_merge2(meeta(add_outerbr))(meetb(sub_outerar))letbwd_sub_nearabr=bwd_subab(unround_nearr)letbwd_sub_upabr=bwd_subab(unround_upr)letbwd_sub_downabr=bwd_subab(unround_downr)letbwd_sub_zeroabr=bwd_subab(unround_zeror)letbwd_sub_anyabr=bwd_subab(unround_anyr)letbwd_sub_noroundabr=bwd_subabr(** Backward subtraction. *)letbwd_mul(a:t)(b:t)(r:t):(t*t)with_bot=(* r = round(a * b) ⇒ ((a = unround(r) / b) ∨ (b = r = 0) ∨ (b unbounded)) ∧
((b = unround(r) / a) ∨ (a = r = 0) ∨ (a unbounded)) *)letaa=ifnot(is_boundedb)||(contains_zerob&&contains_zeror)thenNbaelsemeet_bot(Nba)(div_outerrb)andbb=ifnot(is_boundeda)||(contains_zeroa&&contains_zeror)thenNbbelsemeet_bot(Nbb)(div_outerra)inbot_merge2aabbletbwd_mul_nearabr=bwd_mulab(unround_nearr)letbwd_mul_upabr=bwd_mulab(unround_upr)letbwd_mul_downabr=bwd_mulab(unround_downr)letbwd_mul_zeroabr=bwd_mulab(unround_zeror)letbwd_mul_anyabr=bwd_mulab(unround_anyr)letbwd_mul_noroundabr=bwd_mulabr(** Backward multiplication. *)letbwd_div(a:t)(b:t)(r:t):(t*t)with_bot=(* r = round(a / b) ⇒ ((a = b * unround(r)) ∧ (b = a / unround(r)) ∨ (a = r = 0)) ∨ unbounded) *)ifnot(is_boundeda&&is_boundedb&&is_boundedr)thenNb(a,b)elseletaa=meeta(mul_outerbr)andbb=if(contains_zeroa&&contains_zeror)thenNbbelsemeet_bot(Nbb)(div_outerar)inbot_merge2aabbletbwd_div_nearabr=bwd_divab(unround_nearr)letbwd_div_upabr=bwd_divab(unround_upr)letbwd_div_downabr=bwd_divab(unround_downr)letbwd_div_zeroabr=bwd_divab(unround_zeror)letbwd_div_anyabr=bwd_divab(unround_anyr)letbwd_div_noroundabr=bwd_divabr(** Backward division. *)letbwd_round_int_nearar=meeta(unround_int_nearr)letbwd_round_int_upar=meeta(unround_int_upr)letbwd_round_int_downar=meeta(unround_int_downr)letbwd_round_int_zeroar=meeta(unround_int_zeror)letbwd_round_int_anyar=meeta(unround_int_anyr)letbwd_round_int_noroundar=meetar(** Backward rounding to int. *)letbwd_round_nearar=meeta(unround_nearr)letbwd_round_upar=meeta(unround_upr)letbwd_round_downar=meeta(unround_downr)letbwd_round_zeroar=meeta(unround_zeror)letbwd_round_anyar=meeta(unround_anyr)letbwd_round_noroundar=meetar(** Backward rounding from real. *)letbwd_square(a:t)(r:t):t_with_bot=letrr=sqrt_outerrinjoin_bot(meet_bot(Nba)rr)(meet_bot(Nba)(bot_lift1negrr))letbwd_square_nearar=bwd_squarea(unround_nearr)letbwd_square_upar=bwd_squarea(unround_upr)letbwd_square_downar=bwd_squarea(unround_downr)letbwd_square_zeroar=bwd_squarea(unround_zeror)letbwd_square_anyar=bwd_squarea(unround_anyr)letbwd_square_noroundar=bwd_squarear(** Backward square. *)letbwd_sqrt(a:t)(r:t):t_with_bot=meeta(square_outerr)letbwd_sqrt_nearar=bwd_sqrta(unround_nearr)letbwd_sqrt_upar=bwd_sqrta(unround_upr)letbwd_sqrt_downar=bwd_sqrta(unround_downr)letbwd_sqrt_zeroar=bwd_sqrta(unround_zeror)letbwd_sqrt_anyar=bwd_sqrta(unround_anyr)letbwd_sqrt_noroundar=bwd_sqrtar(** Backward square root. *)letbwd_of_zunroundloupr=letr=unroundrinifF.is_finiter.lo&&F.is_finiter.upthenletlo=Z.maxlo(Z.of_floatr.lo)andup=Z.minup(Z.of_floatr.up)inifZ.leqloupthenNb(lo,up)elseBOTelseNb(lo,up)letbwd_of_z_nearloupr=bwd_of_zunround_int_nearlouprletbwd_of_z_uploupr=bwd_of_zunround_int_uplouprletbwd_of_z_downloupr=bwd_of_zunround_int_downlouprletbwd_of_z_zeroloupr=bwd_of_zunround_int_zerolouprletbwd_of_z_anyloupr=bwd_of_zunround_int_anylouprletbwd_of_z_noroundloupr=bwd_of_z(funx->x)loupr(** Backward conversion from int. *)letbwd_to_z(lo:Z.t)(up:Z.t)(r:t):t_with_bot=bwd_round_int_zeror(mk(FF.of_z_downlo)(FF.of_z_upup))(** Backward conversion to int. *)letmeet_nonzero(a:t):t_with_bot=letlo=ifa.lo=0.thenFF.min_denormalelsea.loandup=ifa.up=0.then-.FF.min_denormalelsea.upinof_float_botloup(** Keeps only non-zero elements. *)end(** Intervals with rounding to double. *)moduleSingle=structmoduleFF=F.Single(** {2 Arithmetic} *)letadd_near:t->t->t=wrap_op2add_sgl_itv_nearletadd_up:t->t->t=wrap_op2add_sgl_itv_upletadd_down:t->t->t=wrap_op2add_sgl_itv_downletadd_zero:t->t->t=wrap_op2add_sgl_itv_zeroletadd_outer:t->t->t=wrap_op2add_sgl_itv_outerletadd_inner:t->t->t_with_bot=wrap_op2_botadd_sgl_itv_inner(** Addition. *)letsub_near:t->t->t=wrap_op2sub_sgl_itv_nearletsub_up:t->t->t=wrap_op2sub_sgl_itv_upletsub_down:t->t->t=wrap_op2sub_sgl_itv_downletsub_zero:t->t->t=wrap_op2sub_sgl_itv_zeroletsub_outer:t->t->t=wrap_op2sub_sgl_itv_outerletsub_inner:t->t->t_with_bot=wrap_op2_botsub_sgl_itv_inner(** Subtraction. *)letmul_near:t->t->t=wrap_op2mul_sgl_itv_nearletmul_up:t->t->t=wrap_op2mul_sgl_itv_upletmul_down:t->t->t=wrap_op2mul_sgl_itv_downletmul_zero:t->t->t=wrap_op2mul_sgl_itv_zeroletmul_outer:t->t->t=wrap_op2mul_sgl_itv_outerletmul_inner:t->t->t_with_bot=wrap_op2_botmul_sgl_itv_inner(** Multiplication. *)letdivpos_near:t->t->t=wrap_op2divpos_sgl_itv_nearletdivpos_up:t->t->t=wrap_op2divpos_sgl_itv_upletdivpos_down:t->t->t=wrap_op2divpos_sgl_itv_downletdivpos_zero:t->t->t=wrap_op2divpos_sgl_itv_zeroletdivpos_outer:t->t->t=wrap_op2divpos_sgl_itv_outerletdivpos_inner:t->t->t_with_bot=wrap_op2_botdivpos_sgl_itv_inner(* Division by a divisor of constant sign. *)letdiv_unmerged_near:t->t->tlist=wrap_div_unmergeddivpos_nearletdiv_unmerged_up:t->t->tlist=wrap_div_unmergeddivpos_upletdiv_unmerged_down:t->t->tlist=wrap_div_unmergeddivpos_downletdiv_unmerged_zero:t->t->tlist=wrap_div_unmergeddivpos_zeroletdiv_unmerged_outer:t->t->tlist=wrap_div_unmergeddivpos_outerletdiv_unmerged_inner:t->t->tlist=wrap_div_unmerged_botdivpos_inner(** Division. Returns a list of 0, 1, or 2 intervals to remain precise. *)letdiv_near:t->t->t_with_bot=wrap_divdivpos_nearletdiv_up:t->t->t_with_bot=wrap_divdivpos_upletdiv_down:t->t->t_with_bot=wrap_divdivpos_downletdiv_zero:t->t->t_with_bot=wrap_divdivpos_zeroletdiv_outer:t->t->t_with_bot=wrap_divdivpos_outerletdiv_inner:t->t->t_with_bot=wrap_div_botdivpos_inner(** Division. Returns a single interval. *)letsquare_near(a:t):t=letaa=absainmul_nearaaaaletsquare_up(a:t):t=letaa=absainmul_upaaaaletsquare_down(a:t):t=letaa=absainmul_downaaaaletsquare_zero(a:t):t=letaa=absainmul_zeroaaaaletsquare_outer(a:t):t=letaa=absainmul_outeraaaaletsquare_inner(a:t):t_with_bot=letaa=absainmul_inneraaaa(** Square. *)letsqrt_near:t->t_with_bot=wrap_sqrtFF.sqrt_nearFF.sqrt_nearletsqrt_up:t->t_with_bot=wrap_sqrtFF.sqrt_upFF.sqrt_upletsqrt_down:t->t_with_bot=wrap_sqrtFF.sqrt_downFF.sqrt_downletsqrt_zero:t->t_with_bot=wrap_sqrtFF.sqrt_zeroFF.sqrt_zeroletsqrt_outer:t->t_with_bot=wrap_sqrtFF.sqrt_downFF.sqrt_upletsqrt_inner:t->t_with_bot=wrap_sqrtFF.sqrt_upFF.sqrt_down(** Square root. Returns the square root of the positive part, possibly ⊥. *)letround_int_near(a:t):t=of_float(FF.round_int_neara.lo)(FF.round_int_neara.up)letround_int_up(a:t):t=of_float(FF.round_int_upa.lo)(FF.round_int_upa.up)letround_int_down(a:t):t=of_float(FF.round_int_downa.lo)(FF.round_int_downa.up)letround_int_zero(a:t):t=of_float(FF.round_int_zeroa.lo)(FF.round_int_zeroa.up)letround_int_outer(a:t):t=of_float(FF.round_int_downa.lo)(FF.round_int_upa.up)letround_int_inner(a:t):t_with_bot=of_float_bot(FF.round_int_upa.lo)(FF.round_int_downa.up)(** Round to integer. *)letunround_int_near(a:t):t=add_outeramhalf_halfletunround_int_up(a:t):t=sub_outerazero_oneletunround_int_down(a:t):t=add_outerazero_oneletunround_int_zero(a:t):t=of_float(ifa.lo<=0.thenFF.sub_downa.lo1.elsea.lo)(ifa.up>=0.thenFF.add_upa.up1.elsea.up)letunround_int_any(a:t):t=add_outeramone_one(** Values that, after rounding to integer in the specified direction, may be in the argument interval.
Useful for backward operators.
*)letunround_near(a:t):t=of_float(FF.preda.lo)(FF.succa.up)letunround_up(a:t):t=of_float(FF.preda.lo)(a.up)letunround_down(a:t):t=of_float(a.lo)(FF.succa.up)letunround_zero(a:t):t=of_float(ifa.lo<=0.thenFF.preda.loelsea.lo)(ifa.up>=0.thenFF.succa.upelsea.up)letunround_any(a:t):t=unround_neara(** Values that, after rounding to float, may be in the argument interval.
Useful for backward operators.
*)letof_int_near(lo:int)(up:int):t=of_float(FF.of_int_nearlo)(FF.of_int_nearup)letof_int_up(lo:int)(up:int):t=of_float(FF.of_int_uplo)(FF.of_int_upup)letof_int_down(lo:int)(up:int):t=of_float(FF.of_int_downlo)(FF.of_int_downup)letof_int_zero(lo:int)(up:int):t=of_float(FF.of_int_zerolo)(FF.of_int_zeroup)letof_int_outer(lo:int)(up:int):t=of_float(FF.of_int_downlo)(FF.of_int_upup)letof_int_inner(lo:int)(up:int):t_with_bot=of_float_bot(FF.of_int_uplo)(FF.of_int_downup)(** Conversion from int. *)letof_int64_near(lo:int64)(up:int64):t=of_float(FF.of_int64_nearlo)(FF.of_int64_nearup)letof_int64_up(lo:int64)(up:int64):t=of_float(FF.of_int64_uplo)(FF.of_int64_upup)letof_int64_down(lo:int64)(up:int64):t=of_float(FF.of_int64_downlo)(FF.of_int64_downup)letof_int64_zero(lo:int64)(up:int64):t=of_float(FF.of_int64_zerolo)(FF.of_int64_zeroup)letof_int64_outer(lo:int64)(up:int64):t=of_float(FF.of_int64_downlo)(FF.of_int64_upup)letof_int64_inner(lo:int64)(up:int64):t_with_bot=of_float_bot(FF.of_int64_uplo)(FF.of_int64_downup)(** Conversion from int64. *)letof_double_near(lo:float)(up:float):t=of_float(FF.of_double_nearlo)(FF.of_double_nearup)letof_double_up(lo:float)(up:float):t=of_float(FF.of_double_uplo)(FF.of_double_upup)letof_double_down(lo:float)(up:float):t=of_float(FF.of_double_downlo)(FF.of_double_downup)letof_double_zero(lo:float)(up:float):t=of_float(FF.of_double_zerolo)(FF.of_double_zeroup)letof_double_outer(lo:float)(up:float):t=of_float(FF.of_double_downlo)(FF.of_double_upup)letof_double_inner(lo:float)(up:float):t_with_bot=of_float_bot(FF.of_double_uplo)(FF.of_double_downup)(** Conversion from double. *)letof_z_near(lo:Z.t)(up:Z.t):t=of_float(FF.of_z_nearlo)(FF.of_z_nearup)letof_z_up(lo:Z.t)(up:Z.t):t=of_float(FF.of_z_uplo)(FF.of_z_upup)letof_z_down(lo:Z.t)(up:Z.t):t=of_float(FF.of_z_downlo)(FF.of_z_downup)letof_z_zero(lo:Z.t)(up:Z.t):t=of_float(FF.of_z_zerolo)(FF.of_z_zeroup)letof_z_outer(lo:Z.t)(up:Z.t):t=of_float(FF.of_z_downlo)(FF.of_z_upup)letof_z_inner(lo:Z.t)(up:Z.t):t_with_bot=of_float_bot(FF.of_z_uplo)(FF.of_z_downup)(** Conversion from Z.t. *)(** {2 Filters} *)(** Given two interval aruments, return the arguments assuming that the predicate holds.
*)letfilter_leq(a:t)(b:t):(t*t)with_bot=bot_merge2(of_float_bota.lo(mina.upb.up))(of_float_bot(maxa.lob.lo)b.up)letfilter_geq(a:t)(b:t):(t*t)with_bot=bot_merge2(of_float_bot(maxa.lob.lo)a.up)(of_float_botb.lo(mina.upb.up))letfilter_lt(a:t)(b:t):(t*t)with_bot=bot_merge2(of_float_bota.lo(mina.up(FF.predb.up)))(of_float_bot(max(FF.succa.lo)b.lo)b.up)letfilter_gt(a:t)(b:t):(t*t)with_bot=bot_merge2(of_float_bot(maxa.lo(FF.succb.lo))a.up)(of_float_botb.lo(min(FF.preda.up)b.up))letfilter_eq(a:t)(b:t):(t*t)with_bot=matchmeetabwithBOT->BOT|Nbx->Nb(x,x)letfilter_neq(a:t)(b:t):(t*t)with_bot=matcha.lo=a.up,b.lo=b.upwith|true,truewhena.lo=b.lo->BOT|true,falsewhena.lo=b.lo->bot_merge2(Nba)(of_float_bot(FF.succb.lo)b.up)|true,falsewhena.up=b.up->bot_merge2(Nba)(of_float_botb.lo(FF.predb.up))|false,truewhena.lo=b.lo->bot_merge2(of_float_bot(FF.succa.lo)a.up)(Nbb)|false,truewhena.up=b.up->bot_merge2(of_float_bota.lo(FF.preda.up))(Nbb)|_->Nb(a,b)(** {2 Backward operations} *)(** Given one or two interval argument(s) and a result interval, return the
argument(s) assuming the result in the operation is in the given result.
*)letbwd_add(a:t)(b:t)(r:t):(t*t)with_bot=(* r = round(a + b) ⇒ a = unround(r) - b ∧ b = unround(r) - a *)bot_merge2(meeta(sub_outerrb))(meetb(sub_outerra))letbwd_add_nearabr=bwd_addab(unround_nearr)letbwd_add_upabr=bwd_addab(unround_upr)letbwd_add_downabr=bwd_addab(unround_downr)letbwd_add_zeroabr=bwd_addab(unround_zeror)letbwd_add_anyabr=bwd_addab(unround_anyr)letbwd_add_noroundabr=bwd_addabr(** Backward addition. *)letbwd_sub(a:t)(b:t)(r:t):(t*t)with_bot=(* r = round(a - b) ⇒ a = b + unround(r) ∧ b = a - unround(r) *)bot_merge2(meeta(add_outerbr))(meetb(sub_outerar))letbwd_sub_nearabr=bwd_subab(unround_nearr)letbwd_sub_upabr=bwd_subab(unround_upr)letbwd_sub_downabr=bwd_subab(unround_downr)letbwd_sub_zeroabr=bwd_subab(unround_zeror)letbwd_sub_anyabr=bwd_subab(unround_anyr)letbwd_sub_noroundabr=bwd_subabr(** Backward subtraction. *)letbwd_mul(a:t)(b:t)(r:t):(t*t)with_bot=(* r = round(a * b) ⇒ ((a = unround(r) / b) ∨ (b = r = 0) ∨ (b unbounded)) ∧
((b = unround(r) / a) ∨ (a = r = 0) ∨ (a unbounded)) *)letaa=ifnot(is_boundedb)||(contains_zerob&&contains_zeror)thenNbaelsemeet_bot(Nba)(div_outerrb)andbb=ifnot(is_boundeda)||(contains_zeroa&&contains_zeror)thenNbbelsemeet_bot(Nbb)(div_outerra)inbot_merge2aabbletbwd_mul_nearabr=bwd_mulab(unround_nearr)letbwd_mul_upabr=bwd_mulab(unround_upr)letbwd_mul_downabr=bwd_mulab(unround_downr)letbwd_mul_zeroabr=bwd_mulab(unround_zeror)letbwd_mul_anyabr=bwd_mulab(unround_anyr)letbwd_mul_noroundabr=bwd_mulabr(** Backward multiplication. *)letbwd_div(a:t)(b:t)(r:t):(t*t)with_bot=(* r = round(a / b) ⇒ ((a = b * unround(r)) ∧ (b = a / unround(r)) ∨ (a = r = 0)) ∨ unbounded) *)ifnot(is_boundeda&&is_boundedb&&is_boundedr)thenNb(a,b)elseletaa=meeta(mul_outerbr)andbb=if(contains_zeroa&&contains_zeror)thenNbbelsemeet_bot(Nbb)(div_outerar)inbot_merge2aabbletbwd_div_nearabr=bwd_divab(unround_nearr)letbwd_div_upabr=bwd_divab(unround_upr)letbwd_div_downabr=bwd_divab(unround_downr)letbwd_div_zeroabr=bwd_divab(unround_zeror)letbwd_div_anyabr=bwd_divab(unround_anyr)letbwd_div_noroundabr=bwd_divabr(** Backward division. *)letbwd_round_int_nearar=meeta(unround_int_nearr)letbwd_round_int_upar=meeta(unround_int_upr)letbwd_round_int_downar=meeta(unround_int_downr)letbwd_round_int_zeroar=meeta(unround_int_zeror)letbwd_round_int_anyar=meeta(unround_int_anyr)letbwd_round_int_noroundar=meetar(** Backward rounding to int. *)letbwd_round_nearar=meeta(unround_nearr)letbwd_round_upar=meeta(unround_upr)letbwd_round_downar=meeta(unround_downr)letbwd_round_zeroar=meeta(unround_zeror)letbwd_round_anyar=meeta(unround_anyr)letbwd_round_noroundar=meetar(** Backward rounding from double or real. *)letbwd_square(a:t)(r:t):t_with_bot=letrr=sqrt_outerrinjoin_bot(meet_bot(Nba)rr)(meet_bot(Nba)(bot_lift1negrr))letbwd_square_nearar=bwd_squarea(unround_nearr)letbwd_square_upar=bwd_squarea(unround_upr)letbwd_square_downar=bwd_squarea(unround_downr)letbwd_square_zeroar=bwd_squarea(unround_zeror)letbwd_square_anyar=bwd_squarea(unround_anyr)letbwd_square_noroundar=bwd_squarear(** Backward square. *)letbwd_sqrt(a:t)(r:t):t_with_bot=meeta(square_outerr)letbwd_sqrt_nearar=bwd_sqrta(unround_nearr)letbwd_sqrt_upar=bwd_sqrta(unround_upr)letbwd_sqrt_downar=bwd_sqrta(unround_downr)letbwd_sqrt_zeroar=bwd_sqrta(unround_zeror)letbwd_sqrt_anyar=bwd_sqrta(unround_anyr)letbwd_sqrt_noroundar=bwd_sqrtar(** Backward square root. *)letbwd_of_zunroundloupr=letr=unroundrinifF.is_finiter.lo&&F.is_finiter.upthenletlo=Z.maxlo(Z.of_floatr.lo)andup=Z.minup(Z.of_floatr.up)inifZ.leqloupthenNb(lo,up)elseBOTelseNb(lo,up)letbwd_of_z_nearloupr=bwd_of_zunround_int_nearlouprletbwd_of_z_uploupr=bwd_of_zunround_int_uplouprletbwd_of_z_downloupr=bwd_of_zunround_int_downlouprletbwd_of_z_zeroloupr=bwd_of_zunround_int_zerolouprletbwd_of_z_anyloupr=bwd_of_zunround_int_anylouprletbwd_of_z_noroundloupr=bwd_of_z(funx->x)loupr(** Backward conversion from int. *)letbwd_to_z(lo:Z.t)(up:Z.t)(r:t):t_with_bot=bwd_round_int_zero(mk(FF.of_z_downlo)(FF.of_z_upup))r(** Backward conversion to int. *)letmeet_nonzero(a:t):t_with_bot=letlo=ifa.lo=0.thenFF.min_denormalelsea.loandup=ifa.up=0.then-.FF.min_denormalelsea.upinof_float_botloup(** Keeps only non-zero elements. *)end(** Intervals with rounding to float. *)(** {2 Operations with rounding mode as argument} *)typeprec=[`SINGLE(** 32-bit single precision *)|`DOUBLE(** 64-bit double precision *)|`REAL(** real arithmetic (outward double rounding is used) *)](** Precision. *)typeround=[`NEAR(** To nearest *)|`UP(** Upwards *)|`DOWN(** Downwards *)|`ZERO(** Towards 0 *)|`ANY(** Any rounding mode *)](** Rounding direction.
This is ignored for real arithmetic.
*)letadd(prec:prec)(round:round)(x:t)(y:t):t=matchprec,roundwith|`SINGLE,`NEAR->Single.add_nearxy|`SINGLE,`UP->Single.add_upxy|`SINGLE,`DOWN->Single.add_downxy|`SINGLE,`ZERO->Single.add_zeroxy|`SINGLE,`ANY->Single.add_outerxy|`DOUBLE,`NEAR->Double.add_nearxy|`DOUBLE,`UP->Double.add_upxy|`DOUBLE,`DOWN->Double.add_downxy|`DOUBLE,`ZERO->Double.add_zeroxy|`DOUBLE,`ANY->Double.add_outerxy|`REAL,_->Double.add_outerxy(** Addition. *)letsub(prec:prec)(round:round)(x:t)(y:t):t=matchprec,roundwith|`SINGLE,`NEAR->Single.sub_nearxy|`SINGLE,`UP->Single.sub_upxy|`SINGLE,`DOWN->Single.sub_downxy|`SINGLE,`ZERO->Single.sub_zeroxy|`SINGLE,`ANY->Single.sub_outerxy|`DOUBLE,`NEAR->Double.sub_nearxy|`DOUBLE,`UP->Double.sub_upxy|`DOUBLE,`DOWN->Double.sub_downxy|`DOUBLE,`ZERO->Double.sub_zeroxy|`DOUBLE,`ANY->Double.sub_outerxy|`REAL,_->Double.sub_outerxy(** Subtraction. *)letmul(prec:prec)(round:round)(x:t)(y:t):t=matchprec,roundwith|`SINGLE,`NEAR->Single.mul_nearxy|`SINGLE,`UP->Single.mul_upxy|`SINGLE,`DOWN->Single.mul_downxy|`SINGLE,`ZERO->Single.mul_zeroxy|`SINGLE,`ANY->Single.mul_outerxy|`DOUBLE,`NEAR->Double.mul_nearxy|`DOUBLE,`UP->Double.mul_upxy|`DOUBLE,`DOWN->Double.mul_downxy|`DOUBLE,`ZERO->Double.mul_zeroxy|`DOUBLE,`ANY->Double.mul_outerxy|`REAL,_->Double.mul_outerxy(** Multiplication. *)letdiv(prec:prec)(round:round)(x:t)(y:t):t_with_bot=matchprec,roundwith|`SINGLE,`NEAR->Single.div_nearxy|`SINGLE,`UP->Single.div_upxy|`SINGLE,`DOWN->Single.div_downxy|`SINGLE,`ZERO->Single.div_zeroxy|`SINGLE,`ANY->Single.div_outerxy|`DOUBLE,`NEAR->Double.div_nearxy|`DOUBLE,`UP->Double.div_upxy|`DOUBLE,`DOWN->Double.div_downxy|`DOUBLE,`ZERO->Double.div_zeroxy|`DOUBLE,`ANY->Double.div_outerxy|`REAL,_->Double.div_outerxy(** Division. *)letdiv_unmerged(prec:prec)(round:round)(x:t)(y:t):tlist=matchprec,roundwith|`SINGLE,`NEAR->Single.div_unmerged_nearxy|`SINGLE,`UP->Single.div_unmerged_upxy|`SINGLE,`DOWN->Single.div_unmerged_downxy|`SINGLE,`ZERO->Single.div_unmerged_zeroxy|`SINGLE,`ANY->Single.div_unmerged_outerxy|`DOUBLE,`NEAR->Double.div_unmerged_nearxy|`DOUBLE,`UP->Double.div_unmerged_upxy|`DOUBLE,`DOWN->Double.div_unmerged_downxy|`DOUBLE,`ZERO->Double.div_unmerged_zeroxy|`DOUBLE,`ANY->Double.div_unmerged_outerxy|`REAL,_->Double.div_unmerged_outerxy(** Division. Returns a list of intervals to remain precise. *)letsquare(prec:prec)(round:round)(x:t):t=matchprec,roundwith|`SINGLE,`NEAR->Single.square_nearx|`SINGLE,`UP->Single.square_upx|`SINGLE,`DOWN->Single.square_downx|`SINGLE,`ZERO->Single.square_zerox|`SINGLE,`ANY->Single.square_outerx|`DOUBLE,`NEAR->Double.square_nearx|`DOUBLE,`UP->Double.square_upx|`DOUBLE,`DOWN->Double.square_downx|`DOUBLE,`ZERO->Double.square_zerox|`DOUBLE,`ANY->Double.square_outerx|`REAL,_->Double.square_outerx(** Square. *)letsqrt(prec:prec)(round:round)(x:t):t_with_bot=matchprec,roundwith|`SINGLE,`NEAR->Single.sqrt_nearx|`SINGLE,`UP->Single.sqrt_upx|`SINGLE,`DOWN->Single.sqrt_downx|`SINGLE,`ZERO->Single.sqrt_zerox|`SINGLE,`ANY->Single.sqrt_outerx|`DOUBLE,`NEAR->Double.sqrt_nearx|`DOUBLE,`UP->Double.sqrt_upx|`DOUBLE,`DOWN->Double.sqrt_downx|`DOUBLE,`ZERO->Double.sqrt_zerox|`DOUBLE,`ANY->Double.sqrt_outerx|`REAL,_->Double.sqrt_outerx(** Square root. *)letround_int(prec:prec)(round:round)(x:t):t=matchprec,roundwith|`SINGLE,`NEAR->Single.round_int_nearx|`SINGLE,`UP->Single.round_int_upx|`SINGLE,`DOWN->Single.round_int_downx|`SINGLE,`ZERO->Single.round_int_zerox|`SINGLE,`ANY->Single.round_int_outerx|`DOUBLE,`NEAR->Double.round_int_nearx|`DOUBLE,`UP->Double.round_int_upx|`DOUBLE,`DOWN->Double.round_int_downx|`DOUBLE,`ZERO->Double.round_int_zerox|`DOUBLE,`ANY->Double.round_int_outerx|`REAL,_->Double.round_int_outerx(** Round to integer. *)letunround_int(prec:prec)(round:round)(x:t):t=matchprec,roundwith|`SINGLE,`NEAR->Single.unround_int_nearx|`SINGLE,`UP->Single.unround_int_upx|`SINGLE,`DOWN->Single.unround_int_downx|`SINGLE,`ZERO->Single.unround_int_zerox|`SINGLE,`ANY->Single.unround_int_anyx|`DOUBLE,`NEAR->Double.unround_int_nearx|`DOUBLE,`UP->Double.unround_int_upx|`DOUBLE,`DOWN->Double.unround_int_downx|`DOUBLE,`ZERO->Double.unround_int_zerox|`DOUBLE,`ANY->Double.unround_int_anyx|`REAL,_->x(** Backward round to integer. *)letround(prec:prec)(round:round)(x:t):t=matchprec,roundwith|`SINGLE,`NEAR->Single.of_double_nearx.lox.up|`SINGLE,`UP->Single.of_double_upx.lox.up|`SINGLE,`DOWN->Single.of_double_downx.lox.up|`SINGLE,`ZERO->Single.of_double_zerox.lox.up|`SINGLE,`ANY->Single.of_double_outerx.lox.up|`DOUBLE,_->x|`REAL,_->x(** Round to float. *)letunround(prec:prec)(round:round)(x:t):t=matchprec,roundwith|`SINGLE,`NEAR->Single.unround_nearx|`SINGLE,`UP->Single.unround_upx|`SINGLE,`DOWN->Single.unround_downx|`SINGLE,`ZERO->Single.unround_zerox|`SINGLE,`ANY->Single.unround_anyx|`DOUBLE,`NEAR->Double.unround_nearx|`DOUBLE,`UP->Double.unround_upx|`DOUBLE,`DOWN->Double.unround_downx|`DOUBLE,`ZERO->Double.unround_zerox|`DOUBLE,`ANY->Double.unround_anyx|`REAL,_->x(** Backward round to float. *)letof_int(prec:prec)(round:round)(x:int)(y:int):t=matchprec,roundwith|`SINGLE,`NEAR->Single.of_int_nearxy|`SINGLE,`UP->Single.of_int_upxy|`SINGLE,`DOWN->Single.of_int_downxy|`SINGLE,`ZERO->Single.of_int_zeroxy|`SINGLE,`ANY->Single.of_int_outerxy|`DOUBLE,`NEAR->Double.of_int_nearxy|`DOUBLE,`UP->Double.of_int_upxy|`DOUBLE,`DOWN->Double.of_int_downxy|`DOUBLE,`ZERO->Double.of_int_zeroxy|`DOUBLE,`ANY->Double.of_int_outerxy|`REAL,_->Double.of_int_outerxy(** Conversion from integer range. *)letof_int64(prec:prec)(round:round)(x:int64)(y:int64):t=matchprec,roundwith|`SINGLE,`NEAR->Single.of_int64_nearxy|`SINGLE,`UP->Single.of_int64_upxy|`SINGLE,`DOWN->Single.of_int64_downxy|`SINGLE,`ZERO->Single.of_int64_zeroxy|`SINGLE,`ANY->Single.of_int64_outerxy|`DOUBLE,`NEAR->Double.of_int64_nearxy|`DOUBLE,`UP->Double.of_int64_upxy|`DOUBLE,`DOWN->Double.of_int64_downxy|`DOUBLE,`ZERO->Double.of_int64_zeroxy|`DOUBLE,`ANY->Double.of_int64_outerxy|`REAL,_->Double.of_int64_outerxy(** Conversion from int64 range. *)letof_z(prec:prec)(round:round)(x:Z.t)(y:Z.t):t=matchprec,roundwith|`SINGLE,`NEAR->Single.of_z_nearxy|`SINGLE,`UP->Single.of_z_upxy|`SINGLE,`DOWN->Single.of_z_downxy|`SINGLE,`ZERO->Single.of_z_zeroxy|`SINGLE,`ANY->Single.of_z_outerxy|`DOUBLE,`NEAR->Double.of_z_nearxy|`DOUBLE,`UP->Double.of_z_upxy|`DOUBLE,`DOWN->Double.of_z_downxy|`DOUBLE,`ZERO->Double.of_z_zeroxy|`DOUBLE,`ANY->Double.of_z_outerxy|`REAL,_->Double.of_z_outerxy(** Conversion from integer range. *)letfilter_leq(prec:prec)(x:t)(y:t):(t*t)with_bot=matchprecwith|`DOUBLE|`REAL->Double.filter_leqxy|`SINGLE->Single.filter_leqxy(** <= filtering. *)letfilter_geq(prec:prec)(x:t)(y:t):(t*t)with_bot=matchprecwith|`DOUBLE|`REAL->Double.filter_geqxy|`SINGLE->Single.filter_geqxy(** >= filtering. *)letfilter_lt(prec:prec)(x:t)(y:t):(t*t)with_bot=matchprecwith|`DOUBLE->Double.filter_ltxy|`SINGLE->Single.filter_ltxy|`REAL->Double.filter_leqxy(** < filtering. *)letfilter_gt(prec:prec)(x:t)(y:t):(t*t)with_bot=matchprecwith|`DOUBLE->Double.filter_gtxy|`SINGLE->Single.filter_gtxy|`REAL->Double.filter_geqxy(** > filtering. *)letfilter_eq(prec:prec)(x:t)(y:t):(t*t)with_bot=matchprecwith|`DOUBLE|`REAL->Double.filter_eqxy|`SINGLE->Single.filter_eqxy(** == filtering. *)letfilter_neq(prec:prec)(x:t)(y:t):(t*t)with_bot=matchprecwith|`DOUBLE->Double.filter_neqxy|`SINGLE->Single.filter_neqxy|`REAL->Nb(x,y)(** != filtering. *)letmeet_nonzero(prec:prec)(x:t):twith_bot=matchprecwith|`DOUBLE->Double.meet_nonzerox|`SINGLE->Single.meet_nonzerox|`REAL->Nbxletbwd_add(prec:prec)(round:round)(x:t)(y:t)(r:t):(t*t)with_bot=matchprec,roundwith|`SINGLE,`NEAR->Single.bwd_add_nearxyr|`SINGLE,`UP->Single.bwd_add_upxyr|`SINGLE,`DOWN->Single.bwd_add_downxyr|`SINGLE,`ZERO->Single.bwd_add_zeroxyr|`SINGLE,`ANY->Single.bwd_add_anyxyr|`DOUBLE,`NEAR->Double.bwd_add_nearxyr|`DOUBLE,`UP->Double.bwd_add_upxyr|`DOUBLE,`DOWN->Double.bwd_add_downxyr|`DOUBLE,`ZERO->Double.bwd_add_zeroxyr|`DOUBLE,`ANY->Double.bwd_add_anyxyr|`REAL,_->Double.bwd_add_noroundxyr(** Backward addition. *)letbwd_sub(prec:prec)(round:round)(x:t)(y:t)(r:t):(t*t)with_bot=matchprec,roundwith|`SINGLE,`NEAR->Single.bwd_sub_nearxyr|`SINGLE,`UP->Single.bwd_sub_upxyr|`SINGLE,`DOWN->Single.bwd_sub_downxyr|`SINGLE,`ZERO->Single.bwd_sub_zeroxyr|`SINGLE,`ANY->Single.bwd_sub_anyxyr|`DOUBLE,`NEAR->Double.bwd_sub_nearxyr|`DOUBLE,`UP->Double.bwd_sub_upxyr|`DOUBLE,`DOWN->Double.bwd_sub_downxyr|`DOUBLE,`ZERO->Double.bwd_sub_zeroxyr|`DOUBLE,`ANY->Double.bwd_sub_anyxyr|`REAL,_->Double.bwd_sub_noroundxyr(** Backward subtraction. *)letbwd_mul(prec:prec)(round:round)(x:t)(y:t)(r:t):(t*t)with_bot=matchprec,roundwith|`SINGLE,`NEAR->Single.bwd_mul_nearxyr|`SINGLE,`UP->Single.bwd_mul_upxyr|`SINGLE,`DOWN->Single.bwd_mul_downxyr|`SINGLE,`ZERO->Single.bwd_mul_zeroxyr|`SINGLE,`ANY->Single.bwd_mul_anyxyr|`DOUBLE,`NEAR->Double.bwd_mul_nearxyr|`DOUBLE,`UP->Double.bwd_mul_upxyr|`DOUBLE,`DOWN->Double.bwd_mul_downxyr|`DOUBLE,`ZERO->Double.bwd_mul_zeroxyr|`DOUBLE,`ANY->Double.bwd_mul_anyxyr|`REAL,_->Double.bwd_mul_noroundxyr(** Backward multiplication. *)letbwd_div(prec:prec)(round:round)(x:t)(y:t)(r:t):(t*t)with_bot=matchprec,roundwith|`SINGLE,`NEAR->Single.bwd_div_nearxyr|`SINGLE,`UP->Single.bwd_div_upxyr|`SINGLE,`DOWN->Single.bwd_div_downxyr|`SINGLE,`ZERO->Single.bwd_div_zeroxyr|`SINGLE,`ANY->Single.bwd_div_anyxyr|`DOUBLE,`NEAR->Double.bwd_div_nearxyr|`DOUBLE,`UP->Double.bwd_div_upxyr|`DOUBLE,`DOWN->Double.bwd_div_downxyr|`DOUBLE,`ZERO->Double.bwd_div_zeroxyr|`DOUBLE,`ANY->Double.bwd_div_anyxyr|`REAL,_->Double.bwd_div_noroundxyr(** Backward division. *)letbwd_round_int(prec:prec)(round:round)(x:t)(r:t):t_with_bot=matchprec,roundwith|`SINGLE,`NEAR->Single.bwd_round_int_nearxr|`SINGLE,`UP->Single.bwd_round_int_upxr|`SINGLE,`DOWN->Single.bwd_round_int_downxr|`SINGLE,`ZERO->Single.bwd_round_int_zeroxr|`SINGLE,`ANY->Single.bwd_round_int_anyxr|`DOUBLE,`NEAR->Double.bwd_round_int_nearxr|`DOUBLE,`UP->Double.bwd_round_int_upxr|`DOUBLE,`DOWN->Double.bwd_round_int_downxr|`DOUBLE,`ZERO->Double.bwd_round_int_zeroxr|`DOUBLE,`ANY->Double.bwd_round_int_anyxr|`REAL,_->Double.bwd_round_int_noroundxr(** Backward rounding to integer. *)letbwd_round(prec:prec)(round:round)(x:t)(r:t):t_with_bot=matchprec,roundwith|`SINGLE,`NEAR->Single.bwd_round_nearxr|`SINGLE,`UP->Single.bwd_round_upxr|`SINGLE,`DOWN->Single.bwd_round_downxr|`SINGLE,`ZERO->Single.bwd_round_zeroxr|`SINGLE,`ANY->Single.bwd_round_anyxr|`DOUBLE,`NEAR->Double.bwd_round_nearxr|`DOUBLE,`UP->Double.bwd_round_upxr|`DOUBLE,`DOWN->Double.bwd_round_downxr|`DOUBLE,`ZERO->Double.bwd_round_zeroxr|`DOUBLE,`ANY->Double.bwd_round_anyxr|`REAL,_->Double.bwd_round_noroundxr(** Backward rounding to float. *)letbwd_square(prec:prec)(round:round)(x:t)(r:t):t_with_bot=matchprec,roundwith|`SINGLE,`NEAR->Single.bwd_square_nearxr|`SINGLE,`UP->Single.bwd_square_upxr|`SINGLE,`DOWN->Single.bwd_square_downxr|`SINGLE,`ZERO->Single.bwd_square_zeroxr|`SINGLE,`ANY->Single.bwd_square_anyxr|`DOUBLE,`NEAR->Double.bwd_square_nearxr|`DOUBLE,`UP->Double.bwd_square_upxr|`DOUBLE,`DOWN->Double.bwd_square_downxr|`DOUBLE,`ZERO->Double.bwd_square_zeroxr|`DOUBLE,`ANY->Double.bwd_square_anyxr|`REAL,_->Double.bwd_square_noroundxr(** Backward square. *)letbwd_sqrt(prec:prec)(round:round)(x:t)(r:t):t_with_bot=matchprec,roundwith|`SINGLE,`NEAR->Single.bwd_sqrt_nearxr|`SINGLE,`UP->Single.bwd_sqrt_upxr|`SINGLE,`DOWN->Single.bwd_sqrt_downxr|`SINGLE,`ZERO->Single.bwd_sqrt_zeroxr|`SINGLE,`ANY->Single.bwd_sqrt_anyxr|`DOUBLE,`NEAR->Double.bwd_sqrt_nearxr|`DOUBLE,`UP->Double.bwd_sqrt_upxr|`DOUBLE,`DOWN->Double.bwd_sqrt_downxr|`DOUBLE,`ZERO->Double.bwd_sqrt_zeroxr|`DOUBLE,`ANY->Double.bwd_sqrt_anyxr|`REAL,_->Double.bwd_sqrt_noroundxr(** Backward square root. *)letbwd_of_z(prec:prec)(round:round)(lo:Z.t)(up:Z.t)(r:t):(Z.t*Z.t)with_bot=matchprec,roundwith|`SINGLE,`NEAR->Single.bwd_of_z_nearloupr|`SINGLE,`UP->Single.bwd_of_z_uploupr|`SINGLE,`DOWN->Single.bwd_of_z_downloupr|`SINGLE,`ZERO->Single.bwd_of_z_zeroloupr|`SINGLE,`ANY->Single.bwd_of_z_anyloupr|`DOUBLE,`NEAR->Double.bwd_of_z_nearloupr|`DOUBLE,`UP->Double.bwd_of_z_uploupr|`DOUBLE,`DOWN->Double.bwd_of_z_downloupr|`DOUBLE,`ZERO->Double.bwd_of_z_zeroloupr|`DOUBLE,`ANY->Double.bwd_of_z_anyloupr|`REAL,_->Double.bwd_of_z_noroundloupr(** Backward conversion from int. *)letbwd_to_z(prec:prec)(lo:Z.t)(up:Z.t)(r:t):t_with_bot=matchprecwith|`DOUBLE|`REAL->Double.bwd_to_zloupr|`SINGLE->Single.bwd_to_zloupr(** Backward conversion to integer. *)