12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328open!BaseopenPrintfletraise_s=Error.raise_sexternalfloat32_of_string:local_string->float32@@portable="caml_float32_of_string"externalof_bits:(int32[@local_opt])->float32@@portable="caml_float32_of_bits_bytecode""caml_float32_of_bits"[@@unboxed][@@noalloc][@@builtin]externalto_bits:(float32[@local_opt])->int32@@portable="caml_float32_to_bits_bytecode""caml_float32_to_bits"[@@unboxed][@@noalloc][@@builtin]moduleUtil=structexternalformat_float32:string->local_float32->string@@portable="caml_format_float32"externalglobalize_float32:local_float32->float32@@portable="%obj_dup"lethash_float32=Hashtbl.hashlethash_fold_float32statet=hash_fold_int32state(to_bitst)letexn_to_stringe=Sexp.to_string_hum(sexp_of_exne)letof_sexp_errorwhatsexp=raise(Sexp.Of_sexp_error(Failurewhat,sexp))letfloat32_of_sexp(sexp:Sexp.t)=matchsexpwith|Atomstr->(tryfloat32_of_stringstrwith|exc->of_sexp_error("float32_of_sexp: "^exn_to_stringexc)sexp)|List_->of_sexp_error"float32_of_sexp: atom needed"sexp;;let%template[@alloca=(heap,stack)]sexp_of_float32n=letatom=format_float32"%.9G"ninSexp.Atomatom[@exclave_if_stacka];;moduleFloat32_replace_polymorphic_compare=structexternal(<):(float32[@local_opt])->(float32[@local_opt])->bool@@portable="%lessthan"external(<=):(float32[@local_opt])->(float32[@local_opt])->bool@@portable="%lessequal"external(<>):(float32[@local_opt])->(float32[@local_opt])->bool@@portable="%notequal"external(=):(float32[@local_opt])->(float32[@local_opt])->bool@@portable="%equal"external(>):(float32[@local_opt])->(float32[@local_opt])->bool@@portable="%greaterthan"external(>=):(float32[@local_opt])->(float32[@local_opt])->bool@@portable="%greaterequal"[%%template[@@@mode.defaultm=(global,local)]externalequal:(float32[@local_opt])->(float32[@local_opt])->bool@@portable="%equal"externalcompare:(float32[@local_opt])->(float32[@local_opt])->int@@portable="%compare"letmax(x:float32)y=letgeq=x>=yinBool.selectgeqxy[@exclave_if_localm];;letmin(x:float32)y=letleq=x<=yinBool.selectleqxy[@exclave_if_localm];;]letascending(x:float32)y=Poly.ascendingxyletdescending(x:float32)y=Poly.descendingxyend[%%template[@@@mode.defaultm=(local,global)]letequal_float32=(Float32_replace_polymorphic_compare.equal[@modem])letcompare_float32=(Float32_replace_polymorphic_compare.compare[@modem])]includestructopenBin_protletbin_shape_float32=Shape.basetype(Shape.Uuid.of_string"float32")[]let%template[@modem=(global,local)]bin_size_float32f=(Size.bin_size_int32_bits[@modem])(to_bitsf);;let%template[@modem=(global,local)]bin_write_float32buf~posf=(Write.bin_write_int32_bits[@modem])buf~pos(to_bitsf);;letbin_read_float32buf~pos_ref=Read.bin_read_int32_bitsbuf~pos_ref|>of_bitslet__bin_read_float32___buf~pos_ref_vint=Common.raise_variant_wrong_type"float32"!pos_ref;;letbin_writer_float32=[%bin_writer:float32]letbin_reader_float32=[%bin_reader:float32]letbin_float32=[%bin_type_class:float32]end(* Stolen from [pervasives.ml]. Adds a "." at the end if needed. It is in
[pervasives.mli], but it also says not to use it directly, so we copy and paste the
code. It makes the assumption on the string passed in argument that it was returned
by [format_float]. *)letvalid_float_lexems=letl=String.lengthsinletrecloopi=ifInt.(>=)ilthens^"."else(matchs.[i]with|'0'..'9'|'-'->loop(i+1)|_->s)inloop0;;endmoduleStable=structmoduleV1=structopenUtiltypet=float32[@@derivingbin_io~localize,compare~localize,equal~localize,globalize,hash,sexp~stackify]letto_stringx=valid_float_lexem(format_float32"%.9g"x)letof_strings=tryfloat32_of_stringswith|_->invalid_argf"Float32.of_string %s"(globalize_strings)();;letstable_witness=Ppx_stable_witness_runtime.Stable_witness.assert_stableendendmoduleT=structincludeStable.V1endincludeTlethashable:tHashable.t={hash;compare;sexp_of_t}include%templateComparator.Make[@modelocal][@modalityportable](T)(* We include type-specific [Replace_polymorphic_compare] at the end, after including
functor application that could shadow its definitions. This is here so that efficient
versions of the comparison functions are exported by this module. *)openUtil.Float32_replace_polymorphic_compareletnan=Stdlib_stable.Float32.nanletinfinity=Stdlib_stable.Float32.infinityletneg_infinity=Stdlib_stable.Float32.neg_infinityletmax_finite_value=Stdlib_stable.Float32.max_floatletepsilon_float=Stdlib_stable.Float32.epsilonexternaladd:local_t->local_t->(t[@local_opt])@@portable="%addfloat32"externalsub:local_t->local_t->(t[@local_opt])@@portable="%subfloat32"externalmul:local_t->local_t->(t[@local_opt])@@portable="%mulfloat32"externaldiv:local_t->local_t->(t[@local_opt])@@portable="%divfloat32"externalneg:local_t->(t[@local_opt])@@portable="%negfloat32"externalabs:local_t->(t[@local_opt])@@portable="%absfloat32"let%template[@modem=(global,local)]squarex=mulxxletis_infx=div1.sx=0.sletis_finitex=subxx=0.sletis_nanx=x<>xexternalpow:local_t->local_t->t@@portable="caml_power_float32_bytecode""powf"[@@unboxed][@@noalloc]externaltrunc:local_t->t@@portable="caml_trunc_float32_bytecode""truncf"[@@unboxed][@@noalloc]letis_integerx=x=truncx&&is_finitexletto_int32_preserve_ordert=ifis_nantthenNoneelseift=0.sthen(* also includes -0. *)Some0lelseift>0.sthenSome(to_bitst)elseSome(Int32.neg(to_bits(negt)));;letto_int32_preserve_order_exnx=Option.value_exn(to_int32_preserve_orderx)letof_int32_preserve_orderx=ifStdlib.(>=)x0lthenof_bitsxelseneg(of_bits(Stdlib.Int32.negx));;letone_ulpdirt=matchto_int32_preserve_ordertwith|None->nan|Somex->of_int32_preserve_order(Int32.(+)x(matchdirwith|`Up->1l|`Down->-1l));;externalmod_float:local_t->local_t->t@@portable="caml_fmod_float32_bytecode""fmodf"[@@unboxed][@@noalloc]externalfrexp:local_t->t*int@@portable="caml_frexp_float32"externalldexp:(t[@unboxed][@local_opt])->(int[@untagged])->(t[@unboxed])@@portable="caml_ldexp_float32_bytecode""caml_ldexp_float32"[@@noalloc]externallog10:local_t->t@@portable="caml_log10_float32_bytecode""log10f"[@@unboxed][@@noalloc]externallog2:local_t->t@@portable="caml_log2_float32_bytecode""log2f"[@@unboxed][@@noalloc]externalexpm1:local_t->t@@portable="caml_expm1_float32_bytecode""expm1f"[@@unboxed][@@noalloc]externallog1p:local_t->t@@portable="caml_log1p_float32_bytecode""log1pf"[@@unboxed][@@noalloc]externalcos:local_t->t@@portable="caml_cos_float32_bytecode""cosf"[@@unboxed][@@noalloc]externalsin:local_t->t@@portable="caml_sin_float32_bytecode""sinf"[@@unboxed][@@noalloc]externaltan:local_t->t@@portable="caml_tan_float32_bytecode""tanf"[@@unboxed][@@noalloc]externalacos:local_t->t@@portable="caml_acos_float32_bytecode""acosf"[@@unboxed][@@noalloc]externalasin:local_t->t@@portable="caml_asin_float32_bytecode""asinf"[@@unboxed][@@noalloc]externalatan:local_t->t@@portable="caml_atan_float32_bytecode""atanf"[@@unboxed][@@noalloc]externalatan2:local_t->local_t->t@@portable="caml_atan2_float32_bytecode""atan2f"[@@unboxed][@@noalloc]externalhypot:local_t->local_t->t@@portable="caml_hypot_float32_bytecode""hypotf"[@@unboxed][@@noalloc]externalcosh:local_t->t@@portable="caml_cosh_float32_bytecode""coshf"[@@unboxed][@@noalloc]externalsinh:local_t->t@@portable="caml_sinh_float32_bytecode""sinhf"[@@unboxed][@@noalloc]externaltanh:local_t->t@@portable="caml_tanh_float32_bytecode""tanhf"[@@unboxed][@@noalloc]externalacosh:local_t->t@@portable="caml_acosh_float32_bytecode""acoshf"[@@unboxed][@@noalloc]externalasinh:local_t->t@@portable="caml_asinh_float32_bytecode""asinhf"[@@unboxed][@@noalloc]externalatanh:local_t->t@@portable="caml_atanh_float32_bytecode""atanhf"[@@unboxed][@@noalloc]externalsqrt:local_t->t@@portable="caml_sqrt_float32_bytecode""sqrtf"[@@unboxed][@@noalloc][@@builtin]externalcbrt:local_t->t@@portable="caml_cbrt_float32_bytecode""cbrtf"[@@unboxed][@@noalloc]externalexp:local_t->t@@portable="caml_exp_float32_bytecode""expf"[@@unboxed][@@noalloc]externallog:local_t->t@@portable="caml_log_float32_bytecode""logf"[@@unboxed][@@noalloc]externalcopysign:local_t->local_t->t@@portable="caml_copysign_float32_bytecode""copysignf"[@@unboxed][@@noalloc]letis_x_minus_one_exactx=(* [x = x -. 1.] does not work with x87 floating point arithmetic backend (which is used
on 32-bit ocaml) because of 80-bit register precision of intermediate computations.
An alternative way of computing this: [x -. one_ulp `Down x <= 1.] is also prone to
the same precision issues: you need to make sure [x] is 64-bit.
*)not(Int32.equal(to_bitsx)(to_bits(subx1.s)));;letlower_bound_for_intbound_bits=letmin_int_as_float32=ldexp(-1.s)(bound_bits-1)inletopenIntinifbound_bits-1<24(* 24 = #bits in the float's mantissa with sign included *)then((* The smallest float that rounds towards zero to [min_int] is [min_int - 1 + epsilon] *)assert(is_x_minus_one_exactmin_int_as_float32);one_ulp`Up(submin_int_as_float321.s))else((* [min_int_as_float32] is already the smallest float [f] satisfying
[f > min_int - 1]. *)assert(not(is_x_minus_one_exactmin_int_as_float32));min_int_as_float32);;(* [upper_bound_for_int] and [lower_bound_for_int] are for calculating the max/min float
that fits in a given-size integer when rounded towards 0 (using [int_of_float]).
max_int/min_int depend on [num_bits], e.g. +/- 2^30, +/- 2^62 if 31-bit, 63-bit
(respectively) while float is IEEE standard for double (52 significant bits).
In all cases, we want to guarantee that
[lower_bound_for_int <= x <= upper_bound_for_int] iff [int_of_float x] fits in an int
with [num_bits] bits.
[2 ** (num_bits - 1)] is the first float greater that max_int, we use the preceding
float as upper bound.
[- (2 ** (num_bits - 1))] is equal to min_int. For lower bound we look for the smallest
float [f] satisfying [f > min_int - 1] so that [f] rounds toward zero to [min_int]
So in particular we will have: [lower_bound_for_int x <= - (2 ** (1-x))]
[upper_bound_for_int x < 2 ** (1-x) ]
*)letupper_bound_for_intnum_bits=one_ulp`Down(ldexp1.s(num_bits-1))letbox=(* Prevent potential constant folding of [+ (-0.s)] in the near ocamlopt future. The
reason we add [-0.s] rather than [0.s] is that [x + (-0.s)] is always the same as
[x], whereas [x + 0.s] is not, in that it sends [-0.s] to [0.s]. *)letx=Sys.opaque_identity(-0.s)infun(local_f)->add(globalizef)x;;letinvariant(_:t)=()letof_string_opts=trySome(float32_of_strings)with|Failure_->None;;letmax_value=infinityletmin_value=neg_infinityletmin_positive_subnormal_value=pow2.s(-149.s)letmin_positive_normal_value=Stdlib_stable.Float32.min_floatletzero=Stdlib_stable.Float32.zeroletone=Stdlib_stable.Float32.oneletminus_one=Stdlib_stable.Float32.minus_oneletpi=Stdlib_stable.Float32.piletsqrt_pi=0x1.c5bf8ap+0sletsqrt_2pi=0x1.40d932p+1sleteuler_gamma_constant=0x1.2788dp-1sexternalof_int64:(int64[@local_opt])->t@@portable="caml_float32_of_int64_bytecode""caml_float32_of_int64"[@@unboxed][@@noalloc][@@builtin]externalto_int64:(t[@local_opt])->int64@@portable="caml_float32_to_int64_bytecode""caml_float32_to_int64"[@@unboxed][@@noalloc][@@builtin]letiround_lbound=lower_bound_for_intInt.num_bitsletiround_ubound=upper_bound_for_intInt.num_bitsexternalof_float:local_float->(t[@local_opt])@@portable="%float32offloat"externalto_float:local_t->(float[@local_opt])@@portable="%floatoffloat32"externalof_int:int->(t[@local_opt])@@portable="%float32ofint"externalto_int_unchecked:local_t->int@@portable="%intoffloat32"letto_intf=iff>=iround_lbound&&f<=iround_uboundthento_int_uncheckedfelsePrintf.invalid_argf"Float32.to_int: argument (%f) is out of range or NaN"(to_floatf)();;(* Outside of the range (round_nearest_lb..round_nearest_ub), all representable doubles
are integers in the mathematical sense, and [round_nearest] should be identity.
However, for odd numbers with the absolute value between 2**23 and 2**24, the formula
[round_nearest x = floor (x + 0.5)] does not hold:
{v
# let naive_round_nearest x = floor (x +. 0.5s);;
# let x = 2.s ** 23.s +. 1.s;;
val x : float32 = 4503599627370497.s
# naive_round_nearest x;;
- : float32 = 4503599627370498.s
v}
*)letround_nearest_lb=neg(pow2.s23.s)letround_nearest_ub=pow2.s23.s(* For [x = one_ulp `Down 0.5s], the formula [floor (x +. 0.5s)] for rounding to nearest
does not work, because the exact result is halfway between [one_ulp `Down 1.s] and
[1.s], and it gets rounded up to [1.s] due to the round-ties-to-even rule. *)letone_ulp_less_than_half=one_ulp`Down0.5slet[@ocaml.inlinealways]add_half_for_round_nearestt=exclave_addt(ift=one_ulp_less_than_halfthenone_ulp_less_than_half(* since t < 0.5s, make sure the result is < 1.0s *)else0.5s);;moduleRounding_intrinsics=structexternaliround_current:t@local->int64@@portable="caml_simd_cast_float32_int64_bytecode""caml_simd_cast_float32_int64"[@@noalloc][@@unboxed][@@builtin]externalround_current:t@local->t@@portable="caml_simd_float32_round_current_bytecode""caml_simd_float32_round_current"[@@noalloc][@@unboxed][@@builtin]externalround_down:t@local->t@@portable="caml_simd_float32_round_neg_inf_bytecode""caml_simd_float32_round_neg_inf"[@@noalloc][@@unboxed][@@builtin]externalround_up:t@local->t@@portable="caml_simd_float32_round_pos_inf_bytecode""caml_simd_float32_round_pos_inf"[@@noalloc][@@unboxed][@@builtin]externalround_towards_zero:t@local->t@@portable="caml_simd_float32_round_towards_zero_bytecode""caml_simd_float32_round_towards_zero"[@@noalloc][@@unboxed][@@builtin]endletround_down=Rounding_intrinsics.round_downletround_up=Rounding_intrinsics.round_upletround_towards_zero=Rounding_intrinsics.round_towards_zero(* see the comment above [round_nearest_lb] and [round_nearest_ub] for an explanation *)let[@ocaml.inline]round_nearest_inlinet=ift>round_nearest_lb&&t<round_nearest_ubthenround_down(add_half_for_round_nearestt)[@nontail]elseboxt;;letround_nearestt=(round_nearest_inline[@ocaml.inlinedalways])t(* The performance of the "exn" rounding functions is important, so they are written out
separately, and tuned individually. (We could have the option versions call the "exn"
versions, but that imposes arguably gratuitous overhead---especially in the case where
the capture of backtraces is enabled upon "with"---and that seems not worth it when
compared to the relatively small amount of code duplication.) *)letiround_upt=ift>0.0sthen(lett'=round_uptinift'<=iround_uboundthenSome(to_int_uncheckedt')elseNone)elseift>=iround_lboundthenSome(to_int_uncheckedt)elseNone;;let[@ocaml.inlinealways]iround_up_exnt=ift>0.0sthen(lett'=round_uptinift'<=iround_uboundthento_int_uncheckedt'elseinvalid_argf"Float32.iround_up_exn: argument (%f) is too large"(to_floatt)())elseift>=iround_lboundthento_int_uncheckedtelseinvalid_argf"Float32.iround_up_exn: argument (%f) is too small or NaN"(to_floatt)();;letiround_downt=ift>=0.0sthenift<=iround_uboundthenSome(to_int_uncheckedt)elseNoneelse(lett'=round_downtinift'>=iround_lboundthenSome(to_int_uncheckedt')elseNone);;let[@ocaml.inlinealways]iround_down_exnt=ift>=0.0sthenift<=iround_uboundthento_int_uncheckedtelseinvalid_argf"Float32.iround_down_exn: argument (%f) is too large"(to_floatt)()else(lett'=round_downtinift'>=iround_lboundthento_int_uncheckedt'elseinvalid_argf"Float32.iround_down_exn: argument (%f) is too small or NaN"(to_floatt)());;letiround_towards_zerot=ift>=iround_lbound&&t<=iround_uboundthenSome(to_int_uncheckedt)elseNone;;let[@ocaml.inlinealways]iround_towards_zero_exnt=ift>=iround_lbound&&t<=iround_uboundthento_int_uncheckedtelseinvalid_argf"Float32.iround_towards_zero_exn: argument (%f) is out of range or NaN"(to_floatt)();;letiround_nearestt=ift>=0.sthenift<round_nearest_ubthenSome(to_int_unchecked(add_half_for_round_nearestt))elseift<=iround_uboundthenSome(to_int_uncheckedt)elseNoneelseift>round_nearest_lbthenSome(to_int_unchecked(round_down(addt0.5s)))elseift>=iround_lboundthenSome(to_int_uncheckedt)elseNone;;let[@ocaml.inlinealways]iround_nearest_exnt=ift>=0.sthenift<round_nearest_ubthento_int_unchecked(add_half_for_round_nearestt)elseift<=iround_uboundthento_int_uncheckedtelseinvalid_argf"Float32.iround_nearest_exn: argument (%f) is too large"(to_floatt)()elseift>round_nearest_lbthento_int_unchecked(round_down(addt0.5s))elseift>=iround_lboundthento_int_uncheckedtelseinvalid_argf"Float32.iround_nearest_exn: argument (%f) is too small or NaN"(to_floatt)();;(* The following [iround_exn] and [iround] functions are slower than the ones above. Their
equivalence to those functions is tested in the unit tests below. *)let[@inline]iround_exn?(dir=`Nearest)t=matchdirwith|`Zero->iround_towards_zero_exnt|`Nearest->iround_nearest_exnt|`Up->iround_up_exnt|`Down->iround_down_exnt;;letiround?(dir=`Nearest)t=trySome(iround_exn~dirt)with|_->None;;[%%template[@@@mode.defaultm=(global,local)]letmin_inanxy=ifis_nanythenxelseifis_nanxthenyelseifx>ythenxelseyletmax_inanxy=ifis_nanythenxelseifis_nanxthenyelseifx>ythenxelsey]moduleParts:sigtypetvalfractional:t->float32@@portablevalintegral:t->float32@@portableend=structtypet=float32*float32letfractionalt=fsttletintegralt=sndtendexternalmodf:local_t->Parts.t@@portable="caml_modf_float32"letround_nearest_half_to_even't=ift<=round_nearest_lb||t>=round_nearest_ubthenboxtelse(letfloor=round_downtin(* [ceil_or_succ = if t is an integer then t +. 1.s else ceil t]. Faster than [ceil]. *)letceil_or_succ=addfloor1.sinletdiff_floor=subtfloorinletdiff_ceil=subceil_or_succtinifdiff_floor<diff_ceilthenfloorelseifdiff_floor>diff_ceilthenceil_or_succelseif(* exact tie, pick the even *)mod_floatfloor2.s=0.sthenfloorelseceil_or_succ);;letiround_nearest_half_to_even't=(* The result is unspecified if t is out of range *)round_nearest_half_to_even't|>to_int64;;letround_nearest_half_to_even=(* We only assume the current rounding mode is half-to-even in native code. *)matchSys.backend_typewith|Native->Rounding_intrinsics.round_current|_->round_nearest_half_to_even';;letiround_nearest_half_to_even=(* We only assume the current rounding mode is half-to-even in native code. *)matchSys.backend_typewith|Native->Rounding_intrinsics.iround_current|_->iround_nearest_half_to_even';;letround?(dir=`Nearest)t=matchdirwith|`Nearest->round_nearestt|`Down->round_downt|`Up->round_upt|`Zero->round_towards_zerot;;externalclassify_float:(t[@unboxed][@local_opt])->Stdlib.fpclass@@portable="caml_classify_float32_bytecode""caml_classify_float32"[@@noalloc]letclassifyt=letmoduleC=Float.Classinmatchclassify_floattwith|FP_normal->C.Normal|FP_subnormal->C.Subnormal|FP_zero->C.Zero|FP_infinite->C.Infinite|FP_nan->C.Nan;;letinsert_underscores?(delimiter='_')?(strip_zero=false)string=matchString.lsplit2string~on:'.'with|None->Base.Int_conversions.insert_delimiterstring~delimiter|Some(left,right)->letleft=Base.Int_conversions.insert_delimiterleft~delimiterinletright=ifstrip_zerothenString.rstripright~drop:(func->Char.(=)c'0')elserightin(matchrightwith|""->left|_->left^"."^right);;letto_string_hum?delimiter?(decimals=3)?strip_zero?(explicit_plus=false)f=ifInt.(<)decimals0theninvalid_argf"to_string_hum: invalid argument ~decimals=%d"decimals();matchclassifyfwith|Infinite->iff>0.sthen"inf"else"-inf"|Nan->"nan"|Normal|Subnormal|Zero->lets=ifexplicit_plusthensprintf"%+.*f"decimals(to_floatf)elsesprintf"%.*f"decimals(to_floatf)ininsert_underscoress?delimiter?strip_zero;;letsexp_of_tt=letsexp=sexp_of_ttinmatchDynamic.getSexp.of_float_stylewith|`No_underscores->sexp|`Underscores->(matchsexpwith|List_->raise_s(Sexp.message"[sexp_of_float32] produced strange sexp"["sexp",Sexp.sexp_of_tsexp])|Atomstring->ifString.containsstring'E'thensexpelseAtom(insert_underscoresstring));;letto_padded_compact_string_customt?(prefix="")~kilo~mega~giga~tera?peta()=(* Round a ratio toward the nearest integer, resolving ties toward the nearest even
number. For sane inputs (in particular, when [denominator] is an integer and
[abs numerator < 2e52]) this should be accurate. Otherwise, the result might be a
little bit off, but we don't really use that case. *)letiround_ratio_exn~numerator~denominator=letk=round_down(divnumeratordenominator)in(* if [abs k < 2e53], then both [k] and [k +. 1.] are accurately represented, and in
particular [k +. 1. > k]. If [denominator] is also an integer, and
[abs (denominator *. (k +. 1)) < 2e53] (and in some other cases, too), then [lower]
and [higher] are actually both accurate. Since (roughly)
[numerator = denominator *. k] then for [abs numerator < 2e52] we should be fine. *)letlower=muldenominatorkinlethigher=muldenominator(addk1.s)in(* Subtracting numbers within a factor of two from each other is accurate. So either
the two subtractions below are accurate, or k = 0, or k = -1. In case of a tie,
round to even. *)letdiff_right=subhighernumeratorinletdiff_left=subnumeratorlowerinletk=iround_nearest_exnkinifdiff_right<diff_leftthenk+1elseifdiff_right>diff_leftthenkelseif(* a tie *)Int.O.(kland1=0)thenkelsek+1inmatchclassifytwith|Infinite->ift<0.0sthen"-inf "else"inf "|Nan->"nan "|Subnormal|Normal|Zero->letgot=letconv_onet=assert(0.s<=t&&t<999.95s);letx=prefix^Util.format_float32"%.1f"tin(* Fix the ".0" suffix *)ifString.is_suffixx~suffix:".0"then(letx=Bytes.of_stringxinletn=Bytes.lengthxinBytes.setx(n-1)' ';Bytes.setx(n-2)' ';Bytes.unsafe_to_string~no_mutation_while_string_reachable:x)elsexinletconvmagtdenominator=assert((denominator=100.s&&t>=999.95s)||(denominator>=100_000.s&&t>=round_nearest(muldenominator9.999_5s)));assert(t<round_nearest(muldenominator9_999.5s));leti,d=letk=iround_ratio_exn~numerator:t~denominatorin(* [mod] is okay here because we know i >= 0. *)k/10,Stdlib.(mod)k10inletopenIntinassert(0<=i&&i<1000);assert(0<=d&&d<10);ifd=0thensprintf"%s%d%s "prefiximagelsesprintf"%s%d%s%d"prefiximagdin(* While the standard metric prefixes (e.g. capital "M" rather than "m", [1]) are
nominally more correct, this hinders readability in our case. E.g., 10G6 and 1066
look too similar. That's an extreme example, but in general k,m,g,t,p probably
stand out better than K,M,G,T,P when interspersed with digits.
[1] http://en.wikipedia.org/wiki/Metric_prefix *)(* The trick here is that:
- the first boundary (999.95) as a float is slightly over-represented (so it is
better approximated as "1k" than as "999.9"),
- the other boundaries are accurately represented, because they are integers.
That's why the strict equalities below do exactly what we want. *)ift<999.95E0sthenconv_onetelseift<999.95E3sthenconvkilot100.selseift<999.95E6sthenconvmegat100_000.selseift<999.95E9sthenconvgigat100_000_000.selseift<999.95E12sthenconvterat100_000_000_000.selse(matchpetawith|None->sprintf"%s%.1e"prefix(to_floatt)|Somepeta->ift<999.95E15sthenconvpetat100_000_000_000_000.selsesprintf"%s%.1e"prefix(to_floatt))inift>=0.sthengotelse"-"^go(negt);;letto_padded_compact_stringt=to_padded_compact_string_customt~kilo:"k"~mega:"m"~giga:"g"~tera:"t"~peta:"p"();;(* Performance note: Initializing the accumulator to 1 results in one extra multiply;
e.g., to compute x ** 4, we in principle only need 2 multiplies, but this function will
have 3 multiplies. However, attempts to avoid this (like decrementing n and
initializing accum to be x, or handling small exponents as a special case) have not
yielded anything that is a net improvement.
*)letint_powxn=letopenIntinifn=0then1.selse((* Using [box x] on the following line convinces the compiler to avoid a certain
boxing (that would result in allocation in each iteration). Soon, the compiler
shouldn't need this "hint" to avoid the boxing. *)letx=ref(boxx)inletn=refninletaccum=ref1.sinif!n<0then((* x ** n = (1/x) ** -n *)x:=div1.s!x;n:=~-(!n);if!n<0then((* n must have been min_int, so it is now so big that it has wrapped around. We
decrement it so that it looks positive again, but accordingly have to put an
extra factor of x in the accumulator.
*)accum:=!x;decrn));(* Letting [a] denote (the original value of) [x ** n], we maintain the invariant that
[(x ** n) *. accum = a]. *)while!n>1doif!nland1<>0thenaccum:=mul!x!accum;x:=mul!x!x;n:=!nlsr1done;(* n is necessarily 1 at this point, so there is one additional multiplication by x. *)mul!x!accum);;letbetweent~low~high=low<=t&&t<=highletclamp_unchecked~to_clamp_maybe_nan~min_which_is_not_nan~max_which_is_not_nan=(* We want to propagate nans; this means we have to use them as the _second_ argument. *)lett_maybe_nan=maxmin_which_is_not_nanto_clamp_maybe_naninminmax_which_is_not_nant_maybe_nan;;letclamp_exnt~min~max=(* Also fails if [min] or [max] is nan *)assert(min<=max);clamp_unchecked~to_clamp_maybe_nan:t~min_which_is_not_nan:min~max_which_is_not_nan:max;;letclampt~min~max=(* Also fails if [min] or [max] is nan *)ifmin<=maxthenOk(clamp_unchecked~to_clamp_maybe_nan:t~min_which_is_not_nan:min~max_which_is_not_nan:max)elseOr_error.error_s(Sexp.message"clamp requires [min <= max]"["min",T.sexp_of_tmin;"max",T.sexp_of_tmax]);;external(+):local_t->local_t->(t[@local_opt])@@portable="%addfloat32"external(-):local_t->local_t->(t[@local_opt])@@portable="%subfloat32"external(*):local_t->local_t->(t[@local_opt])@@portable="%mulfloat32"external(/):local_t->local_t->(t[@local_opt])@@portable="%divfloat32"external(~-):local_t->(t[@local_opt])@@portable="%negfloat32"let(%)xy=ify<0.stheninvalid_argf"%.9g %% %.9g in float32.ml: modulus should be positive"(to_floatx)(to_floaty)()else(letm=mod_floatxyinifm<0.sthenm+yelsem);;let(**)=powlet[@inline]sign_exnt:Sign.t=ift>0.sthenPoselseift<0.sthenNegelseift=0.sthenZeroelseError.raise_s(Sexp.message"Float32.sign_exn of NAN"["",sexp_of_t(globalizet)]);;letsign_or_nant:Sign_or_nan.t=ift>0.sthenPoselseift<0.sthenNegelseift=0.sthenZeroelseNan;;letexponent_bits=8letmantissa_bits=23letexponent_mask32=Int32.(shift_leftoneexponent_bits-one)letexponent_mask=Int.of_int32_truncexponent_mask32letmantissa_mask32=Int32.(shift_leftonemantissa_bits-one)letmantissa_mask=Int.of_int32_truncmantissa_mask32letieee_negativet=letbits=to_bitstinInt32.O.(bits<zero);;letieee_exponentt=letbits=to_bitstinInt32.to_int_truncInt32.O.((bitslsrmantissa_bits)landexponent_mask32);;letieee_mantissat=letbits=to_bitstin(* This is safe because mantissa_mask32 < Int.max_value, even on 32-bit platforms. *)Int.of_int32_truncInt32.O.(bitslandmantissa_mask32);;letcreate_ieee_exn~negative~exponent~mantissa=ifInt.(bit_andexponentexponent_mask<>exponent)thenfailwithf"exponent %d out of range [0, %d]"exponentexponent_mask()elseifInt.(bit_andmantissamantissa_mask<>mantissa)thenfailwithf"mantissa %s out of range [0, %s]"(Int.to_stringmantissa)(Int.to_stringmantissa_mask)()else(letsign_bits=ifnegativethenInt32.min_valueelseInt32.zeroinletexpt_bits=Int32.shift_left(Int32.of_int_truncexponent)mantissa_bitsinletmant_bits=Int.to_int32_truncmantissainletbits=Int32.(bit_orsign_bits(bit_orexpt_bitsmant_bits))inof_bitsbits);;letcreate_ieee~negative~exponent~mantissa=Or_error.try_with(fun()->create_ieee_exn~negative~exponent~mantissa);;moduleTerse=structtypenonrect=tlett_of_sexp=t_of_sexpletsexp_of_tx=Sexp.Atom(to_stringx)letto_string=to_stringletof_string=of_stringendinclude%templateComparable.With_zero[@modalityportable](structincludeTletzero=zeroend)(* These are partly here as a performance hack to avoid some boxing we're getting with the
versions we get from [With_zero]. They also make [Float32.is_negative nan] and
[Float32.is_non_positive nan] return [false]; the versions we get from [With_zero]
return [true]. *)letis_positivet=t>0.sletis_non_negativet=t>=0.sletis_negativet=t<0.sletis_non_positivet=t<=0.sinclude%templatePretty_printer.Register[@modalityportable](structincludeTletmodule_name="Float32"letto_string=to_stringend)moduleO=structexternal(+):local_t->local_t->(t[@local_opt])@@portable="%addfloat32"external(-):local_t->local_t->(t[@local_opt])@@portable="%subfloat32"external(*):local_t->local_t->(t[@local_opt])@@portable="%mulfloat32"external(/):local_t->local_t->(t[@local_opt])@@portable="%divfloat32"external(~-):local_t->(t[@local_opt])@@portable="%negfloat32"let(%)=(%)let(**)=(**)includeUtil.Float32_replace_polymorphic_compareexternalabs:local_t->(t[@local_opt])@@portable="%absfloat32"externalneg:local_t->(t[@local_opt])@@portable="%negfloat32"letzero=zeroexternalof_int:int->(t[@local_opt])@@portable="%float32ofint"externalof_float:local_float->(t[@local_opt])@@portable="%float32offloat"endmoduleO_dot=structexternal(+.):local_t->local_t->(t[@local_opt])@@portable="%addfloat32"external(-.):local_t->local_t->(t[@local_opt])@@portable="%subfloat32"external(*.):local_t->local_t->(t[@local_opt])@@portable="%mulfloat32"external(/.):local_t->local_t->(t[@local_opt])@@portable="%divfloat32"external(~-.):local_t->(t[@local_opt])@@portable="%negfloat32"let(%.)=(%)let(**.)=(**)end(* Include type-specific [Replace_polymorphic_compare] at the end, after including functor
application that could shadow its definitions. This is here so that efficient versions
of the comparison functions are exported by this module. *)includeUtil.Float32_replace_polymorphic_compare(* These functions specifically replace defaults in replace_polymorphic_compare.
The desired behavior here is to propagate a nan if either argument is nan. Because the
first comparison will always return false if either argument is nan, it suffices to
check if x is nan. Then, when x is nan or both x and y are nan, we return x = nan; and
when y is nan but not x, we return y = nan.
*)letmin(x:t)y=ifx<y||is_nanxthenxelseyletmax(x:t)y=ifx>y||is_nanxthenxelseyinclude%templatestructopenBase_quickcheckletquickcheck_generator=(Generator.map[@modeportable])Generator.float~f:of_floatletquickcheck_observer=(Observer.unmap[@modeportable])Observer.float~f:to_floatletquickcheck_shrinker=(Shrinker.map[@modeportable])Shrinker.float~f:of_float~f_inverse:to_float;;endmodulePrivate=structletbox=boxletlower_bound_for_int=lower_bound_for_intletupper_bound_for_int=upper_bound_for_intletspecialized_hash=Util.hash_float32letone_ulp_less_than_half=one_ulp_less_than_halfletexponent_bits=exponent_bitsletmantissa_bits=mantissa_bitsletexponent_mask32=exponent_mask32letexponent_mask=exponent_maskletmantissa_mask32=mantissa_mask32letmantissa_mask=mantissa_maskendmoduleBytes=structexternalget:bytes->pos:int->float32@@portable="%caml_bytes_getf32"externalunsafe_get:bytes->pos:int->float32@@portable="%caml_bytes_getf32u"externalset:bytes->pos:int->float32->unit@@portable="%caml_bytes_setf32"externalunsafe_set:bytes->pos:int->float32->unit@@portable="%caml_bytes_setf32u"endmoduleString=structexternalget:string->pos:int->float32@@portable="%caml_string_getf32"externalunsafe_get:string->pos:int->float32@@portable="%caml_string_getf32u"endmoduleBigstring=structopenBigarraytypet=(char,int8_unsigned_elt,c_layout)Array1.texternalget:t@shared->pos:int->float32@@portable="%caml_bigstring_getf32"externalunsafe_get:t@shared->pos:int->float32@@portable="%caml_bigstring_getf32u"externalset:t->pos:int->float32->unit@@portable="%caml_bigstring_setf32"externalunsafe_set:t->pos:int->float32->unit@@portable="%caml_bigstring_setf32u"endmoduleBigarray=structopenBigarraymoduleArray1=structexternalget:('a,float32_elt,'c)Array1.t->int->float32@@portable="%caml_ba_float32_ref_1"externalset:('a,float32_elt,'c)Array1.t->int->float32->unit@@portable="%caml_ba_float32_set_1"externalunsafe_get:('a,float32_elt,'c)Array1.t->int->float32@@portable="%caml_ba_float32_unsafe_ref_1"externalunsafe_set:('a,float32_elt,'c)Array1.t->int->float32->unit@@portable="%caml_ba_float32_unsafe_set_1"endmoduleArray2=structexternalget:('a,float32_elt,'c)Array2.t->int->int->float32@@portable="%caml_ba_float32_ref_2"externalset:('a,float32_elt,'c)Array2.t->int->int->float32->unit@@portable="%caml_ba_float32_set_2"externalunsafe_get:('a,float32_elt,'c)Array2.t->int->int->float32@@portable="%caml_ba_float32_unsafe_ref_2"externalunsafe_set:('a,float32_elt,'c)Array2.t->int->int->float32->unit@@portable="%caml_ba_float32_unsafe_set_2"endmoduleArray3=structexternalget:('a,float32_elt,'c)Array3.t->int->int->int->float32@@portable="%caml_ba_float32_ref_3"externalset:('a,float32_elt,'c)Array3.t->int->int->int->float32->unit@@portable="%caml_ba_float32_set_3"externalunsafe_get:('a,float32_elt,'c)Array3.t->int->int->int->float32@@portable="%caml_ba_float32_unsafe_ref_3"externalunsafe_set:('a,float32_elt,'c)Array3.t->int->int->int->float32->unit@@portable="%caml_ba_float32_unsafe_set_3"endend