Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file bigdecimal.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601openCoreopenInt.Replace_polymorphic_compareopenStable_witness.ExportmoduleZ=Zarith.Zletz_ten=Bigint.of_int10|>Bigint.to_zarith_bigintletpow_10_z=(* When performing [Bignum.t -> Bigdecimal.t] conversion, we need to compute the value
[10**(log2 bignum)]. Meanwhile, [log2 (max finite float)] is approximately 1024, so
this seems like a reasonable guess for the upper bound for computations where
performance may matter. Add 17% tip, and you end up with 1200. On the other hand,
1200 words sounds like a sane enough amount of memory for a library to preallocate
statically. If this table fills up, it will take 0.3 MB, which is also not crazy for
something actually being used. *)letmax_memoized_pow=1200inlettbl=Array.create~len:(max_memoized_pow+1)Noneinletpow_10_zn=Z.powz_tenninfunn->ifn>max_memoized_powthenpow_10_znelse(matchtbl.(n)with|Somex->x|None->letx=pow_10_znintbl.(n)<-Somex;x);;letpow_10n=pow_10_zn|>Bigint.of_zarith_bigintletpow_10_bignumn=ifn>=0thenpow_10n|>Bignum.of_bigintelse(letdenom=pow_10(absn)|>Bignum.of_bigintinBignum.(one/denom));;moduleT:sig(** This represents the decimal: mantissa * 10 ^ exponent. An invariant of the type is
that the mantissa is either zero or an integer not divisible by 10. Also, it's
guaranteed that any two distinct decimal numbers will have distinct representations,
which requires in addition that zero is always expressed with an exponent of
zero. *)typet=private{mantissa:Bigint.t;exponent:int}[@@derivingfields,hash,compare]valzero:tvalscale_by:t->power_of_ten:int->tvalcreate:mantissa:Bigint.t->exponent:int->tvalscaling_to_least_common_exponent:t->t->f:(lce:int->mantissa_a:Bigint.t->mantissa_b:Bigint.t->'a)->'amoduleStable:sigmoduleV2:sigtypenonrect=t[@@derivingbin_io,compare,equal,sexp,stable_witness]endmoduleV3:sigtypenonrect=t[@@derivingbin_io,compare,equal,sexp,stable_witness]endendend=struct(* Invariant: [mantissa] is either zero or an integer not divisible by 10. *)typet={mantissa:Bigint.t;exponent:int}[@@derivingfields,hash](* derived compare would be incorrect here *)letscaling_to_least_common_exponentab~f=letlce=mina.exponentb.exponentinletscale_mantissa{mantissa;exponent}=Bigint.(mantissa*pow_10(Int.(-)exponentlce))inf~lce~mantissa_a:(scale_mantissaa)~mantissa_b:(scale_mantissab);;letis_zerot=Bigint.(t.mantissa=zero)letcomparexy=ifx.exponent=y.exponentthenBigint.comparex.mantissay.mantissaelseifis_zeroxthenBigint.(comparezeroy.mantissa)elseifis_zeroythenBigint.(comparex.mantissazero)else(letx_sign=Bigint.signx.mantissainlety_sign=Bigint.signy.mantissainifSign.(<>)x_signy_signthenSign.comparex_signy_signelsescaling_to_least_common_exponentxy~f:(fun~lce:_~mantissa_a~mantissa_b->Bigint.comparemantissa_amantissa_b));;letequal=[%compare.equal:t]moduleStable=structmoduleV2=structtypenonrect=t={mantissa:Bigint.Stable.V1.t;exponent:int}[@@derivingsexp,stable_witness](* derived compare would be incorrect here *)letcompare=compareletequal=equal(** [Bigint] does extra allocation in its binary serialization. Do a simpler version
of what Bignum does and [bin_io] the mantissa as an int, if it fits in an int,
and otherwise as a Bigint. *)moduleBin_rep=structmoduleMantissa=structtypet=|Intofint|BigofBigint.Stable.V1.t[@@derivingbin_io]endtypet={mantissa:Mantissa.t;exponent:int}[@@derivingbin_io]endincludeBinable.Of_binable_without_uuid[@alert"-legacy"](Bin_rep)(structtypenonrect=tletto_binable{mantissa;exponent}=letmantissa=letn=Bigint.to_zarith_bigintmantissainifZ.fits_intnthenBin_rep.Mantissa.Int(Z.to_intn)elseBin_rep.Mantissa.Bigmantissain{Bin_rep.mantissa;exponent};;letof_binable{Bin_rep.mantissa;exponent}=letmantissa=matchmantissawith|Intn->Bigint.of_intn|Bign->nin{mantissa;exponent};;end)endmoduleV3=structtypenonrect=t={mantissa:Bigint.Stable.V2.t;exponent:int}[@@derivingbin_io,sexp,stable_witness](* derived compare would be incorrect here *)letcompare=compareletequal=equalendlet%expect_test"test bin-io digest"=letopenExpect_test_helpers_coreinprint_and_check_stable_type[%here](moduleV2)[];[%expect{| (bin_shape_digest 63dd1de06f1a4e923a03de49c676df55) |}];print_and_check_stable_type[%here](moduleV3)[];[%expect{| (bin_shape_digest 4382b358d87f1333d0277d5af9cfa383) |}];;endletzero={mantissa=Bigint.zero;exponent=0}letcanonicalize~mantissa~exponent=letmantissa=Bigint.to_zarith_bigintmantissainifZ.(equalmantissazero)thenzeroelse((* [go ~mantissa ~exponent n] returns [(mantissa, exponent)] such that mantissa is
not divisible by [10**n], but it may be divisible by [10**k] for some [k < n]. *)letrecgo~mantissa~exponentn=letpow_10_z_n=pow_10_zninletdiv,remainder=Z.div_remmantissapow_10_z_ninifnotZ.(equalremainderzero)thenmantissa,exponentelse(letr=(* Things would still work if instead we did
{[ let r = go ~mantissa ~exponent (n * 2) ]}
But since we already went through the hassle of computing [div], why not
proceed with smaller numbers, saving some work? *)go~mantissa:div~exponent:(exponent+n)(n*2)in(* At this point the highest power of 10 by which mantissa may be divisible is
[n * 2 - 1]. So it is sufficient to test again whether it is still divisible
by [10**n] to bring that number down to [n - 1] or less. *)letmantissa,exponent=rinletdiv,remainder=Z.div_remmantissapow_10_z_ninifZ.(equalremainderzero)thendiv,exponent+nelser)inletmantissa,exponent=go~mantissa~exponent1in{mantissa=Bigint.of_zarith_bigintmantissa;exponent});;letscale_byt~power_of_ten=ifBigint.(t.mantissa=zero)thentelse{mantissa=t.mantissa;exponent=t.exponent+power_of_ten};;letcreate=canonicalizeendincludeTincludeStable.V3letone=create~mantissa:Bigint.one~exponent:0letabs{mantissa;exponent}=create~mantissa:(Bigint.absmantissa)~exponentletneg{mantissa;exponent}=create~mantissa:(Bigint.negmantissa)~exponentletsign{mantissa;exponent=_}=Bigint.signmantissaletis_zerot=Bigint.(t.mantissa=zero)letwith_mantissas_scaled_to_least_exponent~f=scaling_to_least_common_exponent~f:(fun~lce~mantissa_a~mantissa_b->create~mantissa:(fmantissa_amantissa_b)~exponent:lce);;moduleInfix=structlet(*)xy=create~mantissa:(Bigint.(*)x.mantissay.mantissa)~exponent:(x.exponent+y.exponent);;let(+)xy=ifx.exponent=y.exponentthencreate~mantissa:(Bigint.(+)x.mantissay.mantissa)~exponent:x.exponentelseifis_zeroxthenyelseifis_zeroythenxelsewith_mantissas_scaled_to_least_exponent~f:Bigint.(+)xy;;let(-)xy=ifx.exponent=y.exponentthencreate~mantissa:(Bigint.(-)x.mantissay.mantissa)~exponent:x.exponentelseifis_zeroxthennegyelseifis_zeroythenxelsewith_mantissas_scaled_to_least_exponent~f:Bigint.(-)xy;;endletof_bigintn=create~mantissa:n~exponent:0letof_intn=create~mantissa:(Bigint.of_intn)~exponent:0letto_bignum{mantissa;exponent}=letfactor=pow_10_bignumexponentinletmantissa=Bignum.of_bigintmantissainBignum.(mantissa*factor);;(* Determines the number of digits after the period, returning None if the part after the
decimal isn't a simple integer, e.g., doesn't match the pattern {v [_1-9]* v}. This is
specifically to catch the case where the part after the decimal starts with a ['-'].
*)letnum_decimal_digits_and_mantissas=letnot_underscore=function|'_'->false|_->trueinmatchString.indexs'.'with|None->Some(0,s)|Somedot->letdecimal_part=String.subos~pos:(dot+1)|>String.filter~f:not_underscorein(* This rejects strings like "123.-123" *)ifnot(String.for_alldecimal_part~f:Char.is_digit)thenNoneelse(letnum_decimal_digits=String.lengthdecimal_partinletint_part=String.subos~len:dotinSome(num_decimal_digits,int_part^decimal_part));;letof_string_base10s=Bigint.of_zarith_bigint(Z.of_string_base10s)(* [of_string_without_exponent] accepts the following formats.
- (-|+)?[0-9][0-9_]*.[0-9_]*
- (-|+)?.[0-9][0-9_]*
*)letof_string_without_exponents=letunparseable()=raise_s[%message"Can't be parsed as Bigdecimal"~_:(s:string)]in(* Explicitly disallow strings without any digits as zarith currently accepts [""] and
["-"] as zero. That is: [Bigint.(of_string "" = zero) && Bigint.(of_string "-" =
zero)]. *)ifnot(String.existss~f:Char.is_digit)thenunparseable();matchnum_decimal_digits_and_mantissaswith|None->unparseable()|Some(num_decimal_digits,mantissa)->letmantissa=tryof_string_base10mantissawith|_->unparseable()increate~mantissa~exponent:(Int.negnum_decimal_digits);;letof_strings=matchString.rfindis~f:(fun_c->matchcwith|'e'|'E'->true|_->false)with|None->of_string_without_exponents|Somee_pos->letsignificand=String.subs~pos:0~len:e_posinletouter_exponent=String.subos~pos:(e_pos+1)|>Int.of_stringinlet{mantissa;exponent}=of_string_without_exponentsignificandincreate~mantissa~exponent:(Int.(+)exponentouter_exponent);;letto_string_no_sn({mantissa;exponent}ast)=if[%compare.equal:t]tzerothen"0"else(letis_neg,mantissa=ifBigint.is_negativemantissathentrue,Bigint.negmantissaelsefalse,mantissainletmantissa_string=Bigint.to_stringmantissainletmantissa_string_length=String.lengthmantissa_stringinletdecimal_location=Int.(+)mantissa_string_lengthexponentinletsign=ifis_negthen"-"else""inmatchOrdering.of_intdecimal_locationwith|Equal->(* 0.12345 *)sign^"0."^mantissa_string|Greater->ifexponent<0then((* decimal point inside the mantissa string, e.g. 123.45 *)letint_part,decimal_part=(String.slicemantissa_string0decimal_location,String.slicemantissa_stringdecimal_locationmantissa_string_length)insign^int_part^"."^decimal_part)else((* right-pad with [exponent] zeroes, e.g. 1234500 *)letrpad=String.makeexponent'0'insign^mantissa_string^rpad)|Less->(* zeros between decimal and mantissa_string, e.g. 0.0012345 *)letnum_zeros=-1*decimal_locationinletlpad=String.makenum_zeros'0'insign^"0."^lpad^mantissa_string);;letto_string_no_sn_grouping?(sep=',')t=letstr=to_string_no_sntin(* now add separators to make it readable *)letend_of_int_part=Option.value(String.indexstr'.')~default:(String.lengthstr)inletint_digits_and_seps=letrecgoacci=function|0->acc|>String.of_char_list|n->letdigit=str.[n-1]inletacc=ifi%3=0&&i>0&&Char.(<>)digit'-'thendigit::sep::accelsedigit::accingoacc(i+1)(n-1)ingo[]0end_of_int_partinint_digits_and_seps^String.subostr~pos:end_of_int_part;;letround_to_bigint_internal~dirt=(* Only fails for zero denominator, which can't happen in this case. *)Bignum.round_as_bigint_exn?dir(to_bignumt);;letround_to_bigint?dirt=round_to_bigint_internal~dirtletround?dirt=create~mantissa:(round_to_bigint_internal~dirt)~exponent:0letto_int_exnt=ifis_zerotthen0elseifInt.is_negativet.exponentthenfailwithf!"to_int_exn not integral: %{#no_sn}"t()else((* Use [Bigint.( * )] since [Int.( * )] doesn't raise on overflow *)tryBigint.(*)t.mantissa(pow_10t.exponent)|>Bigint.to_int_exnwith|_->failwithf!"to_int_exn overflow: %{#no_sn}"t());;letto_intt=trySome(to_int_exnt)with|_->None;;letto_floatt=to_string_no_snt|>Float.of_stringletof_float_short_exnx=Float.to_stringx|>of_stringletof_float_shortx=Or_error.try_with(fun()->of_float_short_exnx)letpower_of_ten_which_is_a_multiple_ofx=(* This function returns [Some (z, 10**z) ] iff
{[
2**k * 5**n = x
]}
Otherwise, it returns None.
In such case when there is a power of 10 which is in fact a multiple of x, we have:
{[
2**(k + n) <= x
]}
{[
k + n <= floor (log2 x)
]}
{[
x = 2**k * 5**n | 10**(k + n) | 10**(floor(log2 x))
]}
(where "a|b" means "a divides b")
*)letexponent_candidate=Bigint.to_zarith_bigintx|>Zarith.Z.log2inletten_to_exponent_candidate=pow_10exponent_candidateinifBigint.(remten_to_exponent_candidatex=zero)thenSome(exponent_candidate,ten_to_exponent_candidate)elseNone;;letof_bignum_exn=letunrepresentable~bignum=raise_s[%message"Not representable as bigdecimal"~_:(bignum:Bignum.t)]infunbignum->ifBignum.is_zerobignumthenzeroelse(ifBignum.(is_zero(denbignum))thenunrepresentable~bignum;letnum=Bignum.num_as_bigintbignuminletden=Bignum.den_as_bigintbignuminmatchpower_of_ten_which_is_a_multiple_ofdenwith|None->unrepresentable~bignum|Some(exponent,ten_to_exponent)->letmantissa=Bigint.(num*ten_to_exponent/den)increate~mantissa~exponent:(-exponent));;letdiv?(decimals_precision=15)ab=(* If a = m * 10^p and b = n * 10^q, then
a/b = u * 10^r, where
r = p - q, and
u = m / n.
We compute m/n using Bignum.round_decimal to [d] digits, where [d] =
[decimals_precision + r]. The reason is that the result is [u] shifted left by [r]
decimals, so to keep [decimals_precision] decimals after the decimal point, we
compute [m/n] to [decimals_precision + r] places. If [r < 0] then [d <
decimals_precision]: we compute [m/n] to fewer digits because we're going to
shift-right by [abs(r)] afterwards. *)letresult_exponent=a.exponent-b.exponentinletresult_mantissa=letdigits=decimals_precision+result_exponentinBignum.(/)(Bignum.of_biginta.mantissa)(Bignum.of_bigintb.mantissa)|>Bignum.round_decimal~dir:`Nearest~digits|>of_bignum_exninscale_byresult_mantissa~power_of_ten:result_exponent;;letscale_inttn=create~mantissa:(Bigint.(*)t.mantissa(Bigint.of_intn))~exponent:t.exponent;;letround_to_power_of_ten?dirt~power_of_ten=ift.exponent>=power_of_tenthentelse(letmantissa=letpow10=pow_10_bignum(power_of_ten-t.exponent)inletnum=Bignum.of_bigintt.mantissainBignum.(/)numpow10|>Bignum.round_as_bigint_exn?dirincreate~mantissa~exponent:power_of_ten);;letlog10_int_exact{mantissa;exponent}=(* [mantissa] is either zero or an integer not divisible by 10. *)ifBigint.equalmantissaBigint.onethenSomeexponentelseNone;;let[@cold]raise__sqrt_of_negative_numbert=raise_s[%message"Bigdecimal.sqrt got negative argument"(t:t)];;lettwo=of_int2letis_evenn=Int.(n%2=0)letsqrt?(decimals_precision=15)t=ifBigint.is_negativet.mantissathenraise__sqrt_of_negative_numbert;ifis_zerotthenzeroelseifBigint.(t.mantissa=one)&&is_event.exponentthen(* if t = 10^(2*k), then sqrt(t) = 10^k *)create~mantissa:t.mantissa~exponent:(Int.(/)t.exponent2)else((* Babylonian method for computing sqrt
(https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
To compute sqrt(a) to [d] decimal digits of precision:
x_0 = approximate_sqrt(a)
and repeat:
x_(n+1) = (x_n + (a / x_n)) / 2
until |x_(n+1) - x_n| < 10^-d
In order for the result to be accurate to [d] decimals, the division needs to be
accurate to [d + 1] decimals (addition is exact). *)letprecision=create~mantissa:Bigint.one~exponent:(Int.negdecimals_precision)inlet[@inline](/)ab=div~decimals_precision:(decimals_precision+1)abinletx0=refzeroinletx1=ref(Float.sqrt(to_floatt)|>of_float_short_exn)inletopenInfixinlettoo_far()=letdiff=abs(!x0-!x1)incomparediffprecision>=0inwhiletoo_far()dox0:=!x1;x1:=((t/!x0)+!x0)/twodone;round_to_power_of_ten~dir:`Nearest!x1~power_of_ten:(Int.negdecimals_precision));;let(**)tpow=(* Bigint.( ** ) raises a reasonable-looking exception if the power is negative *)create~mantissa:(Bigint.(**)t.mantissa(Bigint.of_intpow))~exponent:(Int.(*)t.exponentpow);;letof_bignumx=Or_error.try_with(fun()->of_bignum_exnx)letis_integralt=t.exponent>=0letto_bigint_exact_exnt=ifnot(is_integralt)thenraise_s[%message"to_bigint_exact_exn: not an integer"(t:t)];Bigint.(*)t.mantissa(Bigint.(**)(Bigint.of_int10)(Bigint.of_intt.exponent));;letto_bigint_exactt=Option.try_with(fun()->to_bigint_exact_exnt)includeInfixincludeSexpable.Of_stringable(structtypenonrect=tletof_string=of_stringletto_string=to_string_no_snend)includeComparable.Make(structtypenonrect=t[@@derivingsexp]letcompare=compareend)includeHashable.Make(structtypenonrect=t[@@derivinghash,sexp,compare]end)