123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(**
Float - Floating-point arihmetics with rounding.
We rely on C code to provide functions with correct rounding
(rounding direction and rounding precision).
*)(** {2 Types} *)typet=floattypebit_float={sign:bool;(** sign bit (true means negative) *)fraction:Z.t;(** fraction bits *)exponent:int;(** exponent (positive, with bias) *)}(** Bit-representation of a float value. *)(** {2 Global rounding direction} *)externalset_round_near:unit->unit="ml_round_near"[@@noalloc]externalset_round_up:unit->unit="ml_round_up"[@@noalloc]externalset_round_down:unit->unit="ml_round_down"[@@noalloc]externalset_round_zero:unit->unit="ml_round_zero"[@@noalloc](**
Set the rounding mode globally.
This affects the behaviors of all floating-point operations, including
OCaml's native float operations, but excluding the operations in this
module (and the float interval module) that specify a rounding direction.
Note that the operations with specified rounding directions may change
the rounding direction globally in some unspecified way, and not reset it
to its former value (this is done for efficiency).
*)(** {2 Operations without rounding} *)letneg(x:t):t=-.xletabs(x:t):t=abs_floatxletfmod(x:t)(y:t):t=mod_floatxy(** Remainder (fmod, not equivalent to IEEE 754's float remainder). *)letinfinite(sign:int):t=ifsign>0theninfinityelseifsign<0thenneg_infinityelse0.(** Constructs an infinity with the given sign. Zero maps to zero. *)(** {2 Predicates} *)letis_nan(x:t):bool=classify_floatx=FP_nanletis_finite(x:t):bool=matchclassify_floatxwith|FP_infinite|FP_nan->false|_->true(** Whether x is finite or not (infinite or NaN). *)letis_infinite(x:t):bool=classify_floatx=FP_infiniteletis_normal(x:t):bool=classify_floatx=FP_normalletis_denormal(x:t):bool=classify_floatx=FP_subnormalletsign(x:t):int=ifx>0.then1elseifx<0.then-1else0(** Sign of x: -1 (negative), 0 (zero or NaN), or 1 (positive). *)letsign_zero(x:t):int=ifx>0.then1elseifx<0.then-1elseif1./.x>0.then1elseif1./.x<0.then-1else0(** As sign, but zero is signed.
Returns -1 (negative or -0), 0 (NaN), or 1 (positive of +0)
*)letequal(x:t)(y:t):bool=x=y(** Equality comparison. Identical to =, i.e., NaN ≠ NaN *)letequal_nan(x:t)(y:t):bool=ifis_nanxthenis_nanyelsex=y(** As equal, but NaN equals NaN (and NaN ≠ non-NaN). *)letleq(x:t)(y:t):bool=x<=yletgeq(x:t)(y:t):bool=x>=yletlt(x:t)(y:t):bool=x<yletgt(x:t)(y:t):bool=x>yleteq(x:t)(y:t):bool=x=yletneq(x:t)(y:t):bool=x<>y(** Comparison predicates. Returns false if one argument is NaN. *)letmin(x:t)(y:t):t=ifx<=ythenxelseyletmax(x:t)(y:t):t=ifx<=ythenyelsex(** Minimum and maximum. *)letis_zero(x:t):bool=signx=0letis_nonzero(x:t):bool=signx<>0letis_positive(x:t):bool=signx>=0letis_negative(x:t):bool=signx<=0letis_positive_strict(x:t):bool=signx>0letis_negative_strict(x:t):bool=signx<0(** Sign predicates. *)(** {2 Printing} *)typeprint_format=unit(** Control the printing of a float (precision, rounding, etc.). *)(* TODO *)letdfl_fmt:print_format=()(** Default format. *)letto_string(fmt:print_format)(x:t):string=ifis_nanxthen"NaN"elseifx=infinitythen"+∞"elseifx=neg_infinitythen"-∞"elsestring_of_float(x+.0.)(* Note: don't remove the "+.0."; it is here to ensure we never print "-0."
TODO: printing a guaranteed under/over-approximation.
*)letprintfmtch(x:t)=output_stringch(to_stringfmtx)letfprintfmtch(x:t)=Format.pp_print_stringch(to_stringfmtx)letbprintfmtch(x:t)=Buffer.add_stringch(to_stringfmtx)(** {2 Operations with specific rounding direction and precision} *)(**
We provide the classic operations (and more) for single and double precision
and all four rounding directions.
*)moduleSingle=struct(** Single precision numbers are stored inside OCaml's floats, that are
actually double precion, but rounding is done to single precision. *)(** {2 Operations} *)externaladd_near:t->t->t="ml_add_sgl_near""ml_add_sgl_near_opt"[@@unboxed][@@noalloc]externaladd_up:t->t->t="ml_add_sgl_up""ml_add_sgl_up_opt"[@@unboxed][@@noalloc]externaladd_down:t->t->t="ml_add_sgl_down""ml_add_sgl_down_opt"[@@unboxed][@@noalloc]externaladd_zero:t->t->t="ml_add_sgl_zero""ml_add_sgl_zero_opt"[@@unboxed][@@noalloc](** Addition *)externalsub_near:t->t->t="ml_sub_sgl_near""ml_sub_sgl_near_opt"[@@unboxed][@@noalloc]externalsub_up:t->t->t="ml_sub_sgl_up""ml_sub_sgl_up_opt"[@@unboxed][@@noalloc]externalsub_down:t->t->t="ml_sub_sgl_down""ml_sub_sgl_down_opt"[@@unboxed][@@noalloc]externalsub_zero:t->t->t="ml_sub_sgl_zero""ml_sub_sgl_zero_opt"[@@unboxed][@@noalloc](** Subtraction *)externalmul_near:t->t->t="ml_mul_sgl_near""ml_mul_sgl_near_opt"[@@unboxed][@@noalloc]externalmul_up:t->t->t="ml_mul_sgl_up""ml_mul_sgl_up_opt"[@@unboxed][@@noalloc]externalmul_down:t->t->t="ml_mul_sgl_down""ml_mul_sgl_down_opt"[@@unboxed][@@noalloc]externalmul_zero:t->t->t="ml_mul_sgl_zero""ml_mul_sgl_zero_opt"[@@unboxed][@@noalloc](** Multiplication *)externalmulz_near:t->t->t="ml_mulz_sgl_near""ml_mulz_sgl_near_opt"[@@unboxed][@@noalloc]externalmulz_up:t->t->t="ml_mulz_sgl_up""ml_mulz_sgl_up_opt"[@@unboxed][@@noalloc]externalmulz_down:t->t->t="ml_mulz_sgl_down""ml_mulz_sgl_down_opt"[@@unboxed][@@noalloc]externalmulz_zero:t->t->t="ml_mulz_sgl_zero""ml_mulz_sgl_zero_opt"[@@unboxed][@@noalloc](** Special multiplication where 0 times an infinity is 0, not NaN.
This is particularly useful for interal bounds.
*)externaldiv_near:t->t->t="ml_div_sgl_near""ml_div_sgl_near_opt"[@@unboxed][@@noalloc]externaldiv_up:t->t->t="ml_div_sgl_up""ml_div_sgl_up_opt"[@@unboxed][@@noalloc]externaldiv_down:t->t->t="ml_div_sgl_down""ml_div_sgl_down_opt"[@@unboxed][@@noalloc]externaldiv_zero:t->t->t="ml_div_sgl_zero""ml_div_sgl_zero_opt"[@@unboxed][@@noalloc](** Division *)externaldivz_near:t->t->t="ml_divz_sgl_near""ml_divz_sgl_near_opt"[@@unboxed][@@noalloc]externaldivz_up:t->t->t="ml_divz_sgl_up""ml_divz_sgl_up_opt"[@@unboxed][@@noalloc]externaldivz_down:t->t->t="ml_divz_sgl_down""ml_divz_sgl_down_opt"[@@unboxed][@@noalloc]externaldivz_zero:t->t->t="ml_divz_sgl_zero""ml_divz_sgl_zero_opt"[@@unboxed][@@noalloc](** Special division where 0 / 0 is 0, not NaN.
This is particularly useful for interal bounds.
*)externalmod_near:t->t->t="ml_mod_sgl_near""ml_mod_sgl_near_opt"[@@unboxed][@@noalloc]externalmod_up:t->t->t="ml_mod_sgl_up""ml_mod_sgl_up_opt"[@@unboxed][@@noalloc]externalmod_down:t->t->t="ml_mod_sgl_down""ml_mod_sgl_down_opt"[@@unboxed][@@noalloc]externalmod_zero:t->t->t="ml_mod_sgl_zero""ml_mod_sgl_zero_opt"[@@unboxed][@@noalloc](** Remainder. *)letsquare_nearx=mul_nearxxletsquare_upx=mul_upxxletsquare_downx=mul_downxxletsquare_zerox=mul_zeroxx(** Square. *)externalsqrt_near:t->t="ml_sqrt_sgl_near""ml_sqrt_sgl_near_opt"[@@unboxed][@@noalloc]externalsqrt_up:t->t="ml_sqrt_sgl_up""ml_sqrt_sgl_up_opt"[@@unboxed][@@noalloc]externalsqrt_down:t->t="ml_sqrt_sgl_down""ml_sqrt_sgl_down_opt"[@@unboxed][@@noalloc]externalsqrt_zero:t->t="ml_sqrt_sgl_zero""ml_sqrt_sgl_zero_opt"[@@unboxed][@@noalloc](** Square root. *)externalround_int_near:t->t="ml_round_int_sgl_near""ml_round_int_sgl_near_opt"[@@unboxed][@@noalloc]externalround_int_up:t->t="ml_round_int_sgl_up""ml_round_int_sgl_up_opt"[@@unboxed][@@noalloc]externalround_int_down:t->t="ml_round_int_sgl_down""ml_round_int_sgl_down_opt"[@@unboxed][@@noalloc]externalround_int_zero:t->t="ml_round_int_sgl_zero""ml_round_int_sgl_zero_opt"[@@unboxed][@@noalloc](** Rounding to an integer. *)externalof_double_near:t->t="ml_to_sgl_near""ml_to_sgl_near_opt"[@@unboxed][@@noalloc]externalof_double_up:t->t="ml_to_sgl_up""ml_to_sgl_up_opt"[@@unboxed][@@noalloc]externalof_double_down:t->t="ml_to_sgl_down""ml_to_sgl_down_opt"[@@unboxed][@@noalloc]externalof_double_zero:t->t="ml_to_sgl_zero""ml_to_sgl_zero_opt"[@@unboxed][@@noalloc](** Rounding from double to single precision. *)externalof_int_near:int->t="ml_of_int_sgl_near""ml_of_int_sgl_near"externalof_int_up:int->t="ml_of_int_sgl_up""ml_of_int_sgl_up"externalof_int_down:int->t="ml_of_int_sgl_down""ml_of_int_sgl_down"externalof_int_zero:int->t="ml_of_int_sgl_zero""ml_of_int_sgl_zero"externalof_int_cur:int->t="ml_of_int_sgl_cur""ml_of_int_sgl_cur"(** Conversion from int with rounding. *)externalof_int64_near:int64->t="ml_of_int64_sgl_near""ml_of_int64_sgl_near_opt"[@@unboxed][@@noalloc]externalof_int64_up:int64->t="ml_of_int64_sgl_up""ml_of_int64_sgl_up_opt"[@@unboxed][@@noalloc]externalof_int64_down:int64->t="ml_of_int64_sgl_down""ml_of_int64_sgl_down_opt"[@@unboxed][@@noalloc]externalof_int64_zero:int64->t="ml_of_int64_sgl_zero""ml_of_int64_sgl_zero_opt"[@@unboxed][@@noalloc]externalof_int64_cur:int64->t="ml_of_int64_sgl_cur""ml_of_int64_sgl_cur_opt"[@@unboxed][@@noalloc](** Conversion from int64 with rounding. *)(**
Code from Zarith to convert from Z.t to float using the current rounding mode.
We add a version for single precision rounding.
*)letround_z_to_singlexexact=letm=Z.to_intxin(* Unless the fractional part is exactly 0, round m to an odd integer *)letm=ifexactthenmelsemlor1in(* Then convert m to float, with the current rounding mode. *)of_int_curmletz_to_singlex=ifObj.is_int(Obj.reprx)then(* Fast path *)of_int_cur(Obj.magicx:int)elsebeginletn=Z.numbitsxinifn<=30thenof_int_cur(Z.to_intx)elsebeginletn=n-26in(* Extract top 26 bits of x *)lettop=Z.shift_rightxnin(* Check if the other bits are all zero *)letexact=Z.equalx(Z.shift_lefttopn)in(* Round to float and apply exponent *)ldexp(round_z_to_singletopexact)nendendletof_z_nearx=set_round_near();z_to_singlexletof_z_upx=set_round_up();z_to_singlexletof_z_downx=set_round_down();z_to_singlexletof_z_zerox=set_round_zero();z_to_singlex(** Conversion from Zarith with rounding. *)(** {2 Floating-point format characteristics} *)letmantissa_bits:int=23(* excluding hidden bit *)letexponent_bits:int=8letexponent_bias:int=127letmin_exponent:int=-126(* for normal values *)letmax_exponent:int=127letnan_infinity_exponent:int=128letmin_denormal:t=ldexp1.(min_exponent-mantissa_bits)letmin_normal:t=ldexp1.min_exponentletmax_normal:t=ldexp(2.-.ldexp1.(-mantissa_bits))max_exponentletmax_exact:t=ldexp1.(mantissa_bits+1)letulp:t=ldexp1.(-mantissa_bits)(** Units in the last place (relative precision). *)letrep_of_bits(i:int32):bit_float={sign=i<0l;exponent=(Int32.to_int(Int32.shift_rightimantissa_bits)land0xff);fraction=Z.of_int32(Int32.logandi0x7fffffl);}letbits_of_rep(r:bit_float):int32=letsign=ifr.signthen0x80000000lelse0linletexp=Int32.shift_left(Int32.of_intr.exponent)mantissa_bitsinInt32.logorsign(Int32.logorexp(Z.to_int32r.fraction))externalto_bits:t->int32="ml_bits_of_float"externalof_bits:int32->t="ml_float_of_bits"letsucc(a:t):t=ifa=neg_infinitythen-.max_normalelseadd_upamin_denormalletpred(a:t):t=ifa=infinitythenmax_normalelsesub_downamin_denormal(** Returns the float immediately follownig or preceeding the argument. *)letsucc_zero(a:t):t=ifa==0.thenaelsesuccaletpred_zero(a:t):t=ifa==0.thenaelsepreda(** As succ and pred, but does not cross zero. *)externalof_string_up:string->t="ml_of_string_sgl_up"externalof_string_down:string->t="ml_of_string_sgl_down"(** Conversion from string. *)end(** Single precision operations. *)moduleDouble=struct(** {2 Operations} *)externaladd_near:t->t->t="ml_add_dbl_near""ml_add_dbl_near_opt"[@@unboxed][@@noalloc]externaladd_up:t->t->t="ml_add_dbl_up""ml_add_dbl_up_opt"[@@unboxed][@@noalloc]externaladd_down:t->t->t="ml_add_dbl_down""ml_add_dbl_down_opt"[@@unboxed][@@noalloc]externaladd_zero:t->t->t="ml_add_dbl_zero""ml_add_dbl_zero_opt"[@@unboxed][@@noalloc](** Addition *)externalsub_near:t->t->t="ml_sub_dbl_near""ml_sub_dbl_near_opt"[@@unboxed][@@noalloc]externalsub_up:t->t->t="ml_sub_dbl_up""ml_sub_dbl_up_opt"[@@unboxed][@@noalloc]externalsub_down:t->t->t="ml_sub_dbl_down""ml_sub_dbl_down_opt"[@@unboxed][@@noalloc]externalsub_zero:t->t->t="ml_sub_dbl_zero""ml_sub_dbl_zero_opt"[@@unboxed][@@noalloc](** Subtraction *)externalmul_near:t->t->t="ml_mul_dbl_near""ml_mul_dbl_near_opt"[@@unboxed][@@noalloc]externalmul_up:t->t->t="ml_mul_dbl_up""ml_mul_dbl_up_opt"[@@unboxed][@@noalloc]externalmul_down:t->t->t="ml_mul_dbl_down""ml_mul_dbl_down_opt"[@@unboxed][@@noalloc]externalmul_zero:t->t->t="ml_mul_dbl_zero""ml_mul_dbl_zero_opt"[@@unboxed][@@noalloc](** Multiplication *)externalmulz_near:t->t->t="ml_mulz_dbl_near""ml_mulz_dbl_near_opt"[@@unboxed][@@noalloc]externalmulz_up:t->t->t="ml_mulz_dbl_up""ml_mulz_dbl_up_opt"[@@unboxed][@@noalloc]externalmulz_down:t->t->t="ml_mulz_dbl_down""ml_mulz_dbl_down_opt"[@@unboxed][@@noalloc]externalmulz_zero:t->t->t="ml_mulz_dbl_zero""ml_mulz_dbl_zero_opt"[@@unboxed][@@noalloc](** Special multiplication where 0 * ∞ is 0, not NaN.
This is particularly useful for interal bounds.
*)externaldiv_near:t->t->t="ml_div_dbl_near""ml_div_dbl_near_opt"[@@unboxed][@@noalloc]externaldiv_up:t->t->t="ml_div_dbl_up""ml_div_dbl_up_opt"[@@unboxed][@@noalloc]externaldiv_down:t->t->t="ml_div_dbl_down""ml_div_dbl_down_opt"[@@unboxed][@@noalloc]externaldiv_zero:t->t->t="ml_div_dbl_zero""ml_div_dbl_zero_opt"[@@unboxed][@@noalloc](** Division *)externaldivz_near:t->t->t="ml_divz_dbl_near""ml_divz_dbl_near_opt"[@@unboxed][@@noalloc]externaldivz_up:t->t->t="ml_divz_dbl_up""ml_divz_dbl_up_opt"[@@unboxed][@@noalloc]externaldivz_down:t->t->t="ml_divz_dbl_down""ml_divz_dbl_down_opt"[@@unboxed][@@noalloc]externaldivz_zero:t->t->t="ml_divz_dbl_zero""ml_divz_dbl_zero_opt"[@@unboxed][@@noalloc](** Special division where 0 / 0 and ∞ / ∞ are 0, not NaN.
This is particularly useful for interal bounds.
*)externalmod_near:t->t->t="ml_mod_dbl_near""ml_mod_dbl_near_opt"[@@unboxed][@@noalloc]externalmod_up:t->t->t="ml_mod_dbl_up""ml_mod_dbl_up_opt"[@@unboxed][@@noalloc]externalmod_down:t->t->t="ml_mod_dbl_down""ml_mod_dbl_down_opt"[@@unboxed][@@noalloc]externalmod_zero:t->t->t="ml_mod_dbl_zero""ml_mod_dbl_zero_opt"[@@unboxed][@@noalloc](** Remainder. *)letsquare_nearx=mul_nearxxletsquare_upx=mul_upxxletsquare_downx=mul_downxxletsquare_zerox=mul_zeroxx(** Square. *)externalsqrt_near:t->t="ml_sqrt_dbl_near""ml_sqrt_dbl_near_opt"[@@unboxed][@@noalloc]externalsqrt_up:t->t="ml_sqrt_dbl_up""ml_sqrt_dbl_up_opt"[@@unboxed][@@noalloc]externalsqrt_down:t->t="ml_sqrt_dbl_down""ml_sqrt_dbl_down_opt"[@@unboxed][@@noalloc]externalsqrt_zero:t->t="ml_sqrt_dbl_zero""ml_sqrt_dbl_zero_opt"[@@unboxed][@@noalloc](** Square root. *)externalround_int_near:t->t="ml_round_int_dbl_near""ml_round_int_dbl_near_opt"[@@unboxed][@@noalloc]externalround_int_up:t->t="ml_round_int_dbl_up""ml_round_int_dbl_up_opt"[@@unboxed][@@noalloc]externalround_int_down:t->t="ml_round_int_dbl_down""ml_round_int_dbl_down_opt"[@@unboxed][@@noalloc]externalround_int_zero:t->t="ml_round_int_dbl_zero""ml_round_int_dbl_zero_opt"[@@unboxed][@@noalloc](** Rounding to an integer. *)externalof_int_near:int->t="ml_of_int_dbl_near""ml_of_int_dbl_near"externalof_int_up:int->t="ml_of_int_dbl_up""ml_of_int_dbl_up"externalof_int_down:int->t="ml_of_int_dbl_down""ml_of_int_dbl_down"externalof_int_zero:int->t="ml_of_int_dbl_zero""ml_of_int_dbl_zero"(** Conversion from int with rounding. *)externalof_int64_near:int64->t="ml_of_int64_dbl_near""ml_of_int64_dbl_near_opt"[@@unboxed][@@noalloc]externalof_int64_up:int64->t="ml_of_int64_dbl_up""ml_of_int64_dbl_up_opt"[@@unboxed][@@noalloc]externalof_int64_down:int64->t="ml_of_int64_dbl_down""ml_of_int64_dbl_down_opt"[@@unboxed][@@noalloc]externalof_int64_zero:int64->t="ml_of_int64_dbl_zero""ml_of_int64_dbl_zero_opt"[@@unboxed][@@noalloc](** Conversion from int64 with rounding. *)letof_z_nearx=set_round_near();Z.to_floatxletof_z_upx=set_round_up();Z.to_floatxletof_z_downx=set_round_down();Z.to_floatxletof_z_zerox=set_round_zero();Z.to_floatx(** Conversion from Zarith with rounding. *)(** {2 Floating-point format characteristics} *)letmantissa_bits:int=52(* excluding hidden bit *)letexponent_bits:int=11letexponent_bias:int=1023letmin_exponent:int=-1022(* for normal values *)letmax_exponent:int=1023letnan_infinity_exponent:int=1024letmin_denormal:t=ldexp1.(min_exponent-mantissa_bits)letmin_normal:t=ldexp1.min_exponentletmax_normal:t=ldexp(2.-.ldexp1.(-mantissa_bits))max_exponentletmax_exact:t=ldexp1.(mantissa_bits+1)letulp:t=ldexp1.(-mantissa_bits)(** Units in the last place (relative precision). *)letrep_of_bits(i:int64):bit_float={sign=i<0L;exponent=(Int64.to_int(Int64.shift_rightimantissa_bits)land0x7ff);fraction=Z.of_int64(Int64.logandi0xfffffffffffffL);}letbits_of_rep(r:bit_float):int64=letsign=ifr.signthen0x8000000000000000Lelse0Linletexp=Int64.shift_left(Int64.of_intr.exponent)mantissa_bitsinInt64.logorsign(Int64.logorexp(Z.to_int64r.fraction))externalto_bits:t->int64="ml_bits_of_double"externalof_bits:int64->t="ml_double_of_bits"letsucc(a:t):t=ifa=neg_infinitythen-.max_normalelseadd_upamin_denormalletpred(a:t):t=ifa=infinitythenmax_normalelsesub_downamin_denormal(** Returns the float immediately following or preceeding the argument. *)letsucc_zero(a:t):t=ifa==0.thenaelsesuccaletpred_zero(a:t):t=ifa==0.thenaelsepreda(** As succ and pred, but does not cross zero. *)externalof_string_up:string->t="ml_of_string_dbl_up"externalof_string_down:string->t="ml_of_string_dbl_down"(** Conversion from string. *)end(** Double precision operations. *)(** {2 Operations with rounding mode as argument} *)typeprec=[`SINGLE(** 32-bit single precision *)|`DOUBLE(** 64-bit double precision *)](** Precision. *)typeround=[`NEAR(** To nearest *)|`UP(** Upwards *)|`DOWN(** Downwards *)|`ZERO(** Towards 0 *)](** Rounding direction. *)letadd(prec:prec)(round:round)xy=matchprec,roundwith|`SINGLE,`NEAR->Single.add_nearxy|`SINGLE,`UP->Single.add_upxy|`SINGLE,`DOWN->Single.add_downxy|`SINGLE,`ZERO->Single.add_zeroxy|`DOUBLE,`NEAR->Double.add_nearxy|`DOUBLE,`UP->Double.add_upxy|`DOUBLE,`DOWN->Double.add_downxy|`DOUBLE,`ZERO->Double.add_zeroxy(** Addition. *)letsub(prec:prec)(round:round)xy=matchprec,roundwith|`SINGLE,`NEAR->Single.sub_nearxy|`SINGLE,`UP->Single.sub_upxy|`SINGLE,`DOWN->Single.sub_downxy|`SINGLE,`ZERO->Single.sub_zeroxy|`DOUBLE,`NEAR->Double.sub_nearxy|`DOUBLE,`UP->Double.sub_upxy|`DOUBLE,`DOWN->Double.sub_downxy|`DOUBLE,`ZERO->Double.sub_zeroxy(** Subtraction. *)letmul(prec:prec)(round:round)xy=matchprec,roundwith|`SINGLE,`NEAR->Single.mul_nearxy|`SINGLE,`UP->Single.mul_upxy|`SINGLE,`DOWN->Single.mul_downxy|`SINGLE,`ZERO->Single.mul_zeroxy|`DOUBLE,`NEAR->Double.mul_nearxy|`DOUBLE,`UP->Double.mul_upxy|`DOUBLE,`DOWN->Double.mul_downxy|`DOUBLE,`ZERO->Double.mul_zeroxy(** Multiplication. *)letmulz(prec:prec)(round:round)xy=matchprec,roundwith|`SINGLE,`NEAR->Single.mulz_nearxy|`SINGLE,`UP->Single.mulz_upxy|`SINGLE,`DOWN->Single.mulz_downxy|`SINGLE,`ZERO->Single.mulz_zeroxy|`DOUBLE,`NEAR->Double.mulz_nearxy|`DOUBLE,`UP->Double.mulz_upxy|`DOUBLE,`DOWN->Double.mulz_downxy|`DOUBLE,`ZERO->Double.mulz_zeroxy(** Multiplication, where 0 * infinity is 0, not Nan. *)letdiv(prec:prec)(round:round)xy=matchprec,roundwith|`SINGLE,`NEAR->Single.div_nearxy|`SINGLE,`UP->Single.div_upxy|`SINGLE,`DOWN->Single.div_downxy|`SINGLE,`ZERO->Single.div_zeroxy|`DOUBLE,`NEAR->Double.div_nearxy|`DOUBLE,`UP->Double.div_upxy|`DOUBLE,`DOWN->Double.div_downxy|`DOUBLE,`ZERO->Double.div_zeroxy(** Division. *)letdivz(prec:prec)(round:round)xy=matchprec,roundwith|`SINGLE,`NEAR->Single.divz_nearxy|`SINGLE,`UP->Single.divz_upxy|`SINGLE,`DOWN->Single.divz_downxy|`SINGLE,`ZERO->Single.divz_zeroxy|`DOUBLE,`NEAR->Double.divz_nearxy|`DOUBLE,`UP->Double.divz_upxy|`DOUBLE,`DOWN->Double.divz_downxy|`DOUBLE,`ZERO->Double.divz_zeroxy(** Division, where 0 / 0 is 0, not Nan. *)letrem(prec:prec)(round:round)xy=matchprec,roundwith|`SINGLE,`NEAR->Single.mod_nearxy|`SINGLE,`UP->Single.mod_upxy|`SINGLE,`DOWN->Single.mod_downxy|`SINGLE,`ZERO->Single.mod_zeroxy|`DOUBLE,`NEAR->Double.mod_nearxy|`DOUBLE,`UP->Double.mod_upxy|`DOUBLE,`DOWN->Double.mod_downxy|`DOUBLE,`ZERO->Double.mod_zeroxy(** Modulo. *)letsquare(prec:prec)(round:round)x=matchprec,roundwith|`SINGLE,`NEAR->Single.square_nearx|`SINGLE,`UP->Single.square_upx|`SINGLE,`DOWN->Single.square_downx|`SINGLE,`ZERO->Single.square_zerox|`DOUBLE,`NEAR->Double.square_nearx|`DOUBLE,`UP->Double.square_upx|`DOUBLE,`DOWN->Double.square_downx|`DOUBLE,`ZERO->Double.square_zerox(** Square. *)letsqrt(prec:prec)(round:round)x=matchprec,roundwith|`SINGLE,`NEAR->Single.sqrt_nearx|`SINGLE,`UP->Single.sqrt_upx|`SINGLE,`DOWN->Single.sqrt_downx|`SINGLE,`ZERO->Single.sqrt_zerox|`DOUBLE,`NEAR->Double.sqrt_nearx|`DOUBLE,`UP->Double.sqrt_upx|`DOUBLE,`DOWN->Double.sqrt_downx|`DOUBLE,`ZERO->Double.sqrt_zerox(** Square root. *)letround_int(prec:prec)(round:round)x=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|`DOUBLE,`NEAR->Double.round_int_nearx|`DOUBLE,`UP->Double.round_int_upx|`DOUBLE,`DOWN->Double.round_int_downx|`DOUBLE,`ZERO->Double.round_int_zerox(** Rounds to integer (the result remains a float). *)letof_int(prec:prec)(round:round)x=matchprec,roundwith|`SINGLE,`NEAR->Single.of_int_nearx|`SINGLE,`UP->Single.of_int_upx|`SINGLE,`DOWN->Single.of_int_downx|`SINGLE,`ZERO->Single.of_int_zerox|`DOUBLE,`NEAR->Double.of_int_nearx|`DOUBLE,`UP->Double.of_int_upx|`DOUBLE,`DOWN->Double.of_int_downx|`DOUBLE,`ZERO->Double.of_int_zerox(** Conversion from int. *)letof_int64(prec:prec)(round:round)x=matchprec,roundwith|`SINGLE,`NEAR->Single.of_int64_nearx|`SINGLE,`UP->Single.of_int64_upx|`SINGLE,`DOWN->Single.of_int64_downx|`SINGLE,`ZERO->Single.of_int64_zerox|`DOUBLE,`NEAR->Double.of_int64_nearx|`DOUBLE,`UP->Double.of_int64_upx|`DOUBLE,`DOWN->Double.of_int64_downx|`DOUBLE,`ZERO->Double.of_int64_zerox(** Conversion from int64. *)letof_z(prec:prec)(round:round)x=matchprec,roundwith|`SINGLE,`NEAR->Single.of_z_nearx|`SINGLE,`UP->Single.of_z_upx|`SINGLE,`DOWN->Single.of_z_downx|`SINGLE,`ZERO->Single.of_z_zerox|`DOUBLE,`NEAR->Double.of_z_nearx|`DOUBLE,`UP->Double.of_z_upx|`DOUBLE,`DOWN->Double.of_z_downx|`DOUBLE,`ZERO->Double.of_z_zerox(** Conversion from Z.t *)letof_string(prec:prec)(round:[`UP|`DOWN])x=matchprec,roundwith|`SINGLE,`UP->Single.of_string_upx|`SINGLE,`DOWN->Single.of_string_downx|`DOUBLE,`UP->Double.of_string_upx|`DOUBLE,`DOWN->Double.of_string_downx(** Conversion from string, with safe rounding. *)letsucc(prec:prec)x=matchprecwith|`SINGLE->Single.succx|`DOUBLE->Double.succx(** Number immediately after. *)letpred(prec:prec)x=matchprecwith|`SINGLE->Single.predx|`DOUBLE->Double.predx(** Number immediately before. *)letsucc_zero(prec:prec)x=matchprecwith|`SINGLE->Single.succ_zerox|`DOUBLE->Double.succ_zerox(** Number immediately after. Does not cross zero. *)letpred_zero(prec:prec)x=matchprecwith|`SINGLE->Single.pred_zerox|`DOUBLE->Double.pred_zerox(** Number immediately before. Does not cross zero. *)letmantissa_bits(prec:prec)=matchprecwith|`SINGLE->Single.mantissa_bits|`DOUBLE->Double.mantissa_bitsletexponent_bits(prec:prec)=matchprecwith|`SINGLE->Single.exponent_bits|`DOUBLE->Double.exponent_bitsletexponent_bias(prec:prec)=matchprecwith|`SINGLE->Single.exponent_bias|`DOUBLE->Double.exponent_biasletmin_exponent(prec:prec)=matchprecwith|`SINGLE->Single.min_exponent|`DOUBLE->Double.min_exponentletmax_exponent(prec:prec)=matchprecwith|`SINGLE->Single.max_exponent|`DOUBLE->Double.max_exponentletnan_infinity_exponent(prec:prec)=matchprecwith|`SINGLE->Single.nan_infinity_exponent|`DOUBLE->Double.nan_infinity_exponentletmin_denormal(prec:prec)=matchprecwith|`SINGLE->Single.min_denormal|`DOUBLE->Double.min_denormalletmin_normal(prec:prec)=matchprecwith|`SINGLE->Single.min_normal|`DOUBLE->Double.min_normalletmax_normal(prec:prec)=matchprecwith|`SINGLE->Single.max_normal|`DOUBLE->Double.max_normalletmax_exact(prec:prec)=matchprecwith|`SINGLE->Single.max_exact|`DOUBLE->Double.max_exactletulp(prec:prec)=matchprecwith|`SINGLE->Single.ulp|`DOUBLE->Double.ulp(** Useful constants. *)letto_bits(prec:prec)(x:float):bit_float=matchprecwith|`SINGLE->x|>Single.to_bits|>Single.rep_of_bits|`DOUBLE->x|>Double.to_bits|>Double.rep_of_bitsletof_bits(prec:prec)(x:bit_float):float=matchprecwith|`SINGLE->x|>Single.bits_of_rep|>Single.of_bits|`DOUBLE->x|>Double.bits_of_rep|>Double.of_bits(** Bit-level extraction. *)