Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file batFloat.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432(*
* BatFloat - Extended floating-point numbers
* Copyright (C) 2007 Bluestorm <bluestorm dot dylc on-the-server gmail dot com>
* 2008 David Teller
*
* This library 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 2.1 of the License, or (at your option) any later version,
* with the special exception on linking described in file LICENSE.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*)openBatNumber##V>=5##modulePervasives=Stdlib(*$inject
##V>=5##module Pervasives = Stdlib
*)moduleBaseFloat=structtypet=floatletzero,one=0.,1.letneg=(~-.)let succx=x+.1.letpredx=x-.1.letabs=abs_floatletadd,sub,mul,div=(+.),(-.),(*.),(/.)letmodulo=mod_floatletpow=(** )let compare=compareletof_int=float_of_intletto_int=int_of_floatlet of_string=float_of_stringletto_string=string_of_floatexternalof_float:float->float ="%identity"externalto_float:float->float ="%identity"endletapprox_equal?(epsilon=1e-5)f1f2=abs_float(f1-.f2)<epsilon(*$T approx_equal
approx_equal 0. 1e-15
approx_equal 0.3333333333 (1. /. 3.)
not (approx_equal 1. 2.)
not (approx_equal 1.5 1.45)
*)externalexp:float->float ="caml_exp_float" "exp"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externallog:float->float="caml_log_float""log"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externallog10:float->float="caml_log10_float""log10"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externalcos:float ->float="caml_cos_float""cos"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externalsin:float->float="caml_sin_float""sin"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externaltan:float->float="caml_tan_float""tan"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externalacos:float->float="caml_acos_float""acos"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externalasin:float->float="caml_asin_float""asin"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externalatan:float->float="caml_atan_float""atan"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externalatan2:float->float->float="caml_atan2_float""atan2"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externalcosh:float->float="caml_cosh_float""cosh"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externalsinh:float->float="caml_sinh_float""sinh"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externaltanh:float->float="caml_tanh_float""tanh"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externalceil:float->float="caml_ceil_float""ceil"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externalfloor:float->float="caml_floor_float""floor"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externalfrexp:float->float*int="caml_frexp_float"externalldexp:float->int->float="caml_ldexp_float"externalmodf:float->float*float="caml_modf_float"letrootmn=ifn<0theninvalid_arg"Float.root: Negative root"elseifm<0.&&nland1<>1theninvalid_arg"Float.root: Imaginary result"elseifm<0.then-.exp(log(abs_floatm)/.(float_of_intn))elseexp(logm/.(float_of_intn))(* sign bit is top bit, shift all other 63 bits away and testif = one
Negative numbers have this bit set, positiveunset.
*)letsignbitx=Int64.shift_right_logical(Int64.bits_of_floatx)63=Int64.one(*$T signbit
signbit (-256.)
not (signbit 1e50)
*)letcopysignxs=ifsignbitsthen-.(abs_floatx)elseabs_floatx(*$T copysign
copysign 1. 1. = 1.
copysign 1. (-1.) = (-1.)
*)letroundx=(* 'halve' is the biggest representable double that is smaller than 0.5;
(halve +. 0.5) rounds to 1., which makes for incorrect rounding of 'halve',
while (halve +. halve) is strictly smaller than 1. as expected. *)lethalve=0.499999999999999944in(* we test x >= 0. rather than x > 0. because otherwise
round_to_string 0. returns "-0." (ceil of-0.5 is 'negative
zero') which is confusing. *)ifx>=0.0thenfloor(x+.halve)elseceil(x-.halve)(* the tests below look ugly with those Pervasives.(...); this is
a temporary fix made necessary by BatFloat overriding the (=)
operator. Hugh. *)(*$T round
(List.map round [1.1; 2.4; 3.3; 3.5; 4.99]) = [1.; 2.; 3.; 4.; 5.]
(List.map round [-1.1; -2.4; -3.3; -3.5; -4.99]) = [-1.; -2.; -3.; -4.; -5.]
round 0.499999999999999944 = 0.
round (-0.499999999999999944) = 0.
*)letround_to_intx=int_of_float(roundx)(*$T round_to_int
(List.map round_to_int [1.1; 2.4; 3.3; 3.5; 4.99]) = [1; 2; 3; 4; 5]
*)moduleInfix=structincludeBatNumber.MakeInfix(BaseFloat)let(=~)=approx_equalendinclude(BatNumber.MakeNumeric(BaseFloat):BatNumber.Numericwithtypet=floatandmoduleInfix:=Infix)letmin(x:float)y=ifx<ythenxelseyletmax(x:float)y=ifx<ythenyelsex(* Fixdefinitions for performance *)external of_float:float->float="%identity"externalto_float:float->float="%identity"externalsqrt:float->float ="caml_sqrt_float""sqrt"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]external neg:float->float ="%negfloat"externalabs:float->float="%absfloat"externalmodulo:float->float->float="caml_fmod_float""fmod"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externalpow:float->float->float="caml_power_float""pow"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]externalof_int:int->float="%floatofint"externalto_int:float->int="%intoffloat"externalof_float:float->float="%identity"externalto_float:float->float="%identity"external(+):t->t->t="%addfloat"external(-):t->t->t="%subfloat"external(*):t->t->t="%mulfloat"external(/):t->t->t="%divfloat"external(**):t->t->t="caml_power_float""pow"##V<4.3##"float"##V>=4.3##[@@unboxed][@@noalloc]typebounded=tletmin_num,max_num=neg_infinity,infinitytypefpkind=Pervasives.fpclass=|FP_normal|FP_subnormal|FP_zero|FP_infinite|FP_nanexternalclassify:float->fpkind="caml_classify_float"letis_nanf=matchclassifyfwith|FP_nan->true|_->falseletis_specialf=matchclassifyfwith|FP_nan|FP_infinite->true|FP_normal|FP_subnormal|FP_zero->falseletis_finitef=matchclassifyfwith|FP_nan|FP_infinite->false|FP_normal|FP_subnormal|FP_zero->trueletinfinity=Pervasives.infinityletneg_infinity=Pervasives.neg_infinityletnan=Pervasives.nanletepsilon=Pervasives.epsilon_floatlete=2.7182818284590452354letlog2e=1.4426950408889634074letlog10e=0.43429448190325182765letln2 =0.69314718055994530942letln10 =2.30258509299404568402letpi=3.14159265358979323846letpi2=1.57079632679489661923letpi4=0.78539816339744830962letinvpi=0.31830988618379067154letinvpi2=0.63661977236758134308letsqrtpi2=1.12837916709551257390letsqrt2=1.41421356237309504880letinvsqrt2=0.70710678118654752440letprintoutt=BatInnerIO.nwriteout(to_stringt)letround_to_string?(digits=0)x=ifPervasives.(<)digits0theninvalid_arg"Float.round_to_string";matchclassifyxwith|FP_normal|FP_subnormal|FP_zero->BatPrintf.sprintf"%.*f"digitsx(* we don't call sprintf in the 'special' cases as itseems to
behave weirdly in some cases (eg. on Windows, bug #191) *)|FP_infinite->ifx=neg_infinitythen"-inf"else"inf"|FP_nan->"nan"(*$T round_to_string
List.mem (round_to_string 3.) ["3."; "3"]
(round_to_string ~digits:0 3.) = (round_to_string 3.)
(round_to_string ~digits:1 3.) = "3.0"
(round_to_string ~digits:1 0.) = "0.0"
(round_to_string ~digits:1 epsilon_float) = "0.0"
(round_to_string ~digits:3 1.23456) = "1.235"
(round_to_string ~digits:3 (- 1.23456)) = "-1.235"
(round_to_string ~digits:3 1.98765) = "1.988"
(round_to_string ~digits:3 (- 1.98765)) = "-1.988"
Result.(catch (round_to_string ~digits:(-1)) 3. |> is_exn (Invalid_argument "Float.round_to_string"))
List.mem (round_to_string 0.5) ["0"; "0."; "1"; "1."]
List.mem (round_to_string (-0.5)) ["-1"; "-1."; "0"; "0."; "-0"; "-0."]
List.mem (round_to_string ~digits:2 0.215) ["0.21"; "0.22"]
List.mem (round_to_string ~digits:2 (-0.215)) ["-0.22"; "-0.21"]
(round_to_string ~digits:32 epsilon_float) = "0.00000000000000022204460492503131"
List.mem (round_to_string ~digits:42 infinity) ["inf"; "infinity"]
List.mem (round_to_string ~digits:0 neg_infinity) ["-inf"; "-infinity"]
List.for_all (fun digits -> (=) "nan" (String.sub (round_to_string~digits nan) 0 3))[0; 42]
*)moduleBase_safe_float=structincludeBaseFloatletif_safex=matchclassifyxwith|FP_infinite->raiseOverflow|FP_nan->raiseNaN|_->()letcheckx=if_safex;xletsafe1fx=check(fx)letsafe2fxy=check (fxy)letadd=safe2addletsub=safe2 subletdiv=safe2 divletmul=safe2 mulletmodulo=safe2moduloletpred=safe1predletsucc=safe1succletpow=safe2 powendmoduleSafe_float=structincludeBatNumber.MakeNumeric(Base_safe_float)letsafe1=Base_safe_float.safe1letsafe2=Base_safe_float.safe2letif_safe=Base_safe_float.if_safeletexp=safe1expletlog=safe1logletlog10=safe1log10letcos=safe1cosletsin=safe1sinlettan=safe1tanletacos=safe1acosletasin=safe1asinletatan=safe1atanletatan2=safe2atan2letcosh =safe1coshletsinh=safe1sinhlettanh=safe1tanhletceil=safe1ceilletfloor=safe1floorletmodfx=let(_,z)asresult=modfxinif_safez;resultletfrexpx=let(f,_)asresult=frexpxinif_safef;resultletldexp=safe2ldexptypebounded=tletmin_num,max_num =neg_infinity,infinitytype fpkind=Pervasives.fpclass=|FP_normal|FP_subnormal|FP_zero|FP_infinite|FP_nanexternalclassify:float->fpkind="caml_classify_float"letis_nan=is_nanletinfinity=Pervasives.infinityletneg_infinity=Pervasives.neg_infinityletnan=Pervasives.nanletepsilon=Pervasives.epsilon_floatletpi=4.*.atan1.externalof_float:float ->float="%identity"externalto_float:float->float="%identity"letprint=printend(*$T succ
is_nan (succ nan)
succ infinity = infinity
succ neg_infinity = neg_infinity
succ (-3.) = -2.
*)(*$T pred
is_nan (pred nan)
pred infinity = infinity
pred neg_infinity = neg_infinity
pred (-3.) = -4.
*)(*$T root
approx_equal (root 9. 2) 3.
approx_equal (root 8. 3) 2.
approx_equal (root 1. 20) 1.
approx_equal (root (-8.) 3) (-2.)
approx_equal (root 0. 6) 0.
approx_equal (root (-0.) 6) 0.
is_nan (root nan 4)
root infinity 4 = infinity
root neg_infinity 3 = neg_infinity
try ignore (root (-8.) 4); false with Invalid_argument _ -> true
try ignore (root neg_infinity 4); false with Invalid_argument _ -> true
try ignore (root (9.) (-2)); false with Invalid_argument _ -> true
*)(*$T is_nan
not (is_nan infinity)
not (is_nan neg_infinity)
not (is_nan (-0.))
not (is_nan 12.)
is_nan nan
*)(*$T is_special
is_special infinity
is_special neg_infinity
not (is_special (-0.))
not (is_special 12.)
is_special nan
*)(*$T is_finite
List.for_all is_finite [1.0; 1e200; 1e-200; 0.0; -0.0; -1.0; -1e200; -1e-200]
not (is_finite nan)
not (is_finite infinity)
not (is_finite neg_infinity)
*)(*$T
try ignore (Safe_float.add 0. infinity); false with BatNumber.Overflow -> true
try ignore (Safe_float.add 0. neg_infinity); false with BatNumber.Overflow -> true
try ignore (Safe_float.add 0. nan); false with BatNumber.NaN -> true
ignore (Safe_float.add 0. (-0.)); true
ignore (Safe_float.add 0. (12.)); true
*)(*$T
try ignore (Safe_float.modf nan); false with Number.NaN -> true
try ignore (Safe_float.modf infinity); false with Number.Overflow -> true
try ignore (Safe_float.modf neg_infinity); false with Number.Overflow -> true
let (frac, int) = Safe_float.modf 3.234 in approx_equal frac 0.234 && approx_equal int 3.
*)