123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342(** Integer arithmetic functions used by [ln], [log10], [exp], and [pow]. *)(* let nbits = Z.numbits *)letz2=Z.of_int2letz10=Z.of_int10letz100=Z.of_int100letzeros=Str.regexp"0+$"(** Unreliable digits at the end of the [log10_digits] calculation *)letunreliable=Str.regexp"[^0]0*$"(** [decimal_lshift_exact n e] is [Some (n * 10 ** e)] if it's an integer, else
[None]. *)letdecimal_lshift_exactne=ifZ.(equalnzero)thenSomenelseifZ.(gtnzero)thenSomeZ.(n*powz10e)else(* val_n = largest power of 10 dividing n. *)letstr_n=Z.(n|>abs|>to_string)inletval_n=String.lengthstr_n-String.length(Str.replace_firstzeros""str_n)inletneg_e=-einifval_n<neg_ethenNoneelseSomeZ.(n/powz10neg_e)letrecsqrt_nearestnab=ifZ.(equalab)thenaelseletneg_n=Z.negnin(sqrt_nearest[@tailcall])nZ.(shift_right_trunc(a-(neg_n/<a))1)a(** [sqrt_nearest n a] is the closest integer to the square root of the positive
integer [n]. [a] is an initial approximation of the square root. Any
positive integer will do for [a], but the closer [a] is to the square root
of [n] the faster convergence will be. *)letsqrt_nearestna=ifZ.Compare.(n<=Z.zero||a<=Z.zero)theninvalid_arg"sqrt_nearest: both arguments should be positive"elsesqrt_nearestnaZ.zero(** [rshift_nearest x shift] is the closest integer to [x / 2**shift], where
[shift] is non-negative. Uses round-to-even in case of a tie. *)letrshift_nearestxshift=letopenZinletb=onelslshiftinletq=xasrshiftinq+if(z2*xland(b-one))+(qlandone)>bthenoneelsezeroletdiv_nearestab=letopenZinletq,r=div_remabinq+if(z2*r)+(qlandone)>bthenoneelsezero(** [ilog ?l x m] is the integer approximation to [m * log (x / m)], with
absolute error boundable in terms only of [x / m].
Given positive integers [x] and [m], return an integer approximation to
[m * log (x / m)]. For [l = 8] and [0.1 <= x / m <= 10] the difference
between the approximation and the exact result is at most 22. For [l = 8]
and [1.0 <= x / m <= 10.0] the difference is at most 15. In both cases
these are upper bounds on the error; it will usually be much smaller. *)letilog?(l=8)xm=letl_minusr=l-!rinletr_minus_lr=!r-linlety=refZ.(x-m)in(* argument reduction; r = number of reductions performed *)letr=ref0inwhile(!r<=l&&Z.(abs!ylsll_minusr>=m))||(!r>l&&Z.(abs!yasrr_minus_lr>=m))doy:=div_nearestZ.((m*!y)lsl1)Z.(m+sqrt_nearest(m*(m+rshift_nearest!y!r))m);incrrdone;lety=!yinletr=!rin(* Taylor series with [t] terms *)lett=-(-10*String.length(Z.to_stringm)/3*l)inletyshift=rshift_nearestyrinletw=ref(div_nearestm(Z.of_intt))infork=t-1downto0doletzk=Z.of_intkinw:=Z.(div_nearestmzk-div_nearest(yshift*!w)m)done;div_nearestZ.(!w*y)mletlog10_digits=ref"23025850929940456840179914546843642076011014886"(** [log10_digits p] is [(floor 10**p) * log 10], given [p >= 0]. For example,
[log10_digits 3] is 2302. *)letlog10_digitsp=letreturn()=p+1|>String.sub!log10_digits0|>Z.of_stringinifp<0theninvalid_arg"p should be non-negative"elseifp>=String.length!log10_digitsthenbegin(* compute p+3, p+6, p+9, ... digits; continue until at least one of the
extra digits is nonzero *)letextra=ref3inletcontinue=reftrueinletdigits=ref""inwhile!continuedo(* compute p+extra digits, correct to within 1ulp *)letm=Z.powz10(p+!extra+2)indigits:=z100|>div_nearest(ilogZ.(z10*m)m)|>Z.to_string;letcheck=Str.regexp(String.make!extra'0'^"$")inifStr.string_matchcheck!digits0thenbeginextra:=!extra+3endelsebegincontinue:=falseenddone;(* keep all reliable digits so far; remove trailing zeroes and next non-zero
digit. *)log10_digits:=Str.replace_firstunreliable""!digits;return()endelsereturn()(** [dlog10 c e p] is an integer approximation of [10**p * log10 (c * 10**e)],
with an absolute error of at most 1. Assumes that:
- [c > 0]
- [p >= 0]
- [c * 10**e] is not exactly 1 *)letdlog10cep=letp=p+2inletl=c|>Z.to_string|>String.lengthinletf=lete_plus_l=e+line_plus_l-ife_plus_l>=1then1else0inletlog_d,log_tenpower=ifp>0thenletm=Z.powz10pinletk=e+p-finletc=ifk>=0thenZ.(c*powz10k)elsediv_nearestc(Z.powz10~-k)inletlog_d=ilogcmin(* error < 5 + 22 = 27 *)letlog_10=log10_digitspin(* error < 1 *)div_nearestZ.(log_d*m)log_10,Z.(of_intf*m)elseZ.zero,div_nearest(Z.of_intf)(Z.powz10~-p)indiv_nearestZ.(log_tenpower+log_d)z100(** [dlog c e p] is an integer approximation of [10**p * log (c * 10 * e)],
with an absolute error of at most 1. Assumes that [c * 10 * e] is not
exactly 1. *)letdlogcep=(* Increase precision by 2. The precision increase is compensated for at the
end with a division by 100. *)letp=p+2in(* Rewrite [c * 10**e] as [d * 10**f] with either f >= 0 and 1 <= d <= 10, or
f <= 0 and 0.1 <= d <= 1. Then we can compute [10**p * log (c * 10 * e)] as
[10**p * log d + 10**p * f * log 10]. *)letl=c|>Z.to_string|>String.lengthinletf=lete_plus_l=e+line_plus_l-ife_plus_l>=1then1else0in(* compute approximation to [10**p * log d], with error < 27 *)letlog_d=ifp>0thenletk=e+p-finletc=ifk>=0thenZ.(c*powz10k)elsediv_nearestc(Z.powz10~-k)(* error of <= 0.5 in c *)in(* ilog magnifies existing error in [c] by a factor of at most 10 *)ilogc(Z.powz10p)else(* [p <= 0]: just approximate the whole thing by 0; error < 2.31 *)Z.zeroinletextra=(f|>abs|>string_of_int|>String.length)-1in(* compute approximation to [f * 10**p*log 10], with error < 11. *)letf_log_ten=iff<>0thenletp_plus_extra=p+extrainifp_plus_extra>=0then(* error in [f * log10_digits (p + extra) < |f| * 1 = |f| *]
after division, [error < |f| / 10**extra + 0.5 < 10 + 0.5 < 11 *)div_nearestZ.(of_intf*log10_digitsp_plus_extra)(Z.powz10extra)elseZ.zeroelseZ.zeroin(* error in sum < 11 + 27 = 38; error after division < 0.38 + 0.5 < 1 *)div_nearestZ.(f_log_ten+log_d)z100(** [iexp ?l x m] is an integer approximation of [m * exp (x / m)], given
[m > 0] and such that [x / m] is small in absolute value. For
[0 <= x / m <= 2.4], the absolute error in the result is bounded by 60 (and
is usually much smaller). *)letiexp?(l=8)xm=(* Find [r] such that [x / 2**r/m <= 2 ** ~-l] *)letr=Z.(numbits((xlsll)/<m))in(* Taylor series. [(2 ** l)**t > m] *)lett=-(-10*String.length(Z.to_stringm)/3*l)inlety=ref(div_nearestx(Z.of_intt))inletmshift=refZ.(mlslr)infori=t-1downto0doletmshift=!mshiftiny:=div_nearestZ.((x*mshift)+!y)Z.(mshift*of_inti)done;(* Expansion *)fork=r-1downto-1doletk_plus_2=k+2in(mshift:=Z.(mlslk_plus_2));lety_val=!yinletmshift=!mshiftiny:=div_nearestZ.(y_val*(y_val+mshift))mshiftdone;Z.(m+!y)(** [dexp c e p] is an approximation to [exp (c * 10**e)], with [p] decimal
places of precision.
Returns integers [d, f] such that:
- [10**(p - 1) <= d <= 10**p], and
- [(d - 1) * 10**f < exp (c * 10**e) < (d + 1) * 10**f]
In other words, [d * 10**f] is an approximation to [exp (c * 10**e)] with
[p] digits of precision, and with an error in [d] of at most [1]. This is
almost, but not quite, the same as the error being < 1ulp: when
[d = 10**(p - 1)] the error could be up to 10 ulp. *)letdexpcep=(* we'll call [iexp] with [m = 10**(p + 2)], giving [p + 3] digits of
precision *)letp=p+2in(* compute [log 10] with extra precision = adjusted exponent of [c * 10**e] *)letextra=max0(e+String.length(Z.to_stringc)-1)inletq=p+extrain(* compute quotient [c * 10**e/(log 10) = c * 10**(e + q)/(log 10 * 10**q)],
rounding down *)letshift=e+qinletcshift=ifshift>=0thenZ.(c*powz10shift)elseletshift=-shiftinZ.(c/<powz10shift)inletquot,rem=Z.(div_remcshift(log10_digitsq))in(* reduce remainder back to original precision *)letrem=div_nearestrem(Z.powz10extra)in(* error in result of [iexp < 120]; error after division < 0.62 *)div_nearest(iexprem(Z.powz10p))(Z.of_int1_000),Z.to_intquot-p+3(** [dpower xc xe yc ye p] is [x ** y], given integers [xc], [xe], [yc], and
[ye] representing decimals [x = xc * 10**xe] and [y = yc * 10**ye]. Returns
a pair of integers [c, e] such that:
- [10**(p - 1) <= c <= 10**p], and
- [(c - 1) * 10**e < x**y < (c + 1) * 10**e]
In other words, [c * 10**e] is an approximation to [x**y] with [p] digits of
precision, and with an error in [c] of at most [1]. This almost, but not
quite, the same as the error being < 1ulp: when [c = 10**(p - 1)] we can
only guarantee error < 10ulp.
We assume that: [x] is positive and not equal to [1], and [y] is nonzero. *)letdpowerxcxeycyep=(* Find [b] such that [10**(b - 1) <= abs y <= 10**b] *)letb=ye+(yc|>Z.abs|>Z.to_string|>String.length)in(* [log x = lxc * 10**(-p - b - 1)], to [p + b + 1] places after the decimal
point *)letlxc=dlogxcxe(p+b+1)in(* compute product
[y * log x = yc * lxc * 10**(-p - b - 1 + ye) = pc * 10**(-p - 1)] *)letshift=ye-binletpc=ifshift>=0thenZ.(lxc*yc*powz10shift)elseletshift=-shiftindiv_nearestZ.(lxc*yc)(Z.powz10shift)inletcoef,exp=ifZ.(equalpczero)then(* we prefer a result that isn't exactly 1; this makes it easier to
compute a correctly rounded result in [pow] *)if(xc|>Z.to_string|>String.length)+xe>=1=Z.(gtyczero)thenletp_minus_1=p-1inZ.(powz10p_minus_1+one),1-pelseZ.(powz10p-one),-pelseletcoef,exp=dexppc~-(p+1)(p+1)inletcoef=div_nearestcoefz10incoef,succexpincoef,exp(** [log10_lb ?correction c] is a lower bound for [100 * log10 c] for a positive
integer [c]. *)letlog10_lb?(correction=['1',100;'2',70;'3',53;'4',40;'5',31;'6',23;'7',16;'8',10;'9',5])c=ifZ.(Compare.(c<=zero))theninvalid_arg"log10_lb: argument should be non-negative"elseletstr_c=Z.to_stringcin(100*String.lengthstr_c)-List.assocstr_c.[0]correction