├── .gitignore ├── LICENSE.md ├── README.md ├── exp.go ├── exp_test.go ├── go.mod ├── log.go ├── log_test.go ├── misc.go ├── misc_test.go ├── pow.go └── pow_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # -*- mode: gitignore; -*- 2 | *~ 3 | \#*\# 4 | /.emacs.desktop 5 | /.emacs.desktop.lock 6 | *.elc 7 | auto-save-list 8 | tramp 9 | .\#* 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Alberto Donizetti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Package bigfloat provides arbitrary-precision natural logarithm and 2 | exponentiation for the standard library's `big.Float` type. 3 | 4 | [![GoDoc](https://godoc.org/github.com/ALTree/bigfloat?status.png)](https://godoc.org/github.com/ALTree/bigfloat) 5 | 6 | The package requires Go 1.10 or newer. 7 | 8 | #### Example 9 | 10 | ```go 11 | package main 12 | 13 | import ( 14 | "fmt" 15 | "math/big" 16 | 17 | "github.com/ALTree/bigfloat" 18 | ) 19 | 20 | // We'll compute the value of the transcendental number 2^√2, also 21 | // known as the Gelfond–Schneider constant, to 1000 bits. 22 | func main() { 23 | const prec = 1000 // in binary digits 24 | two := big.NewFloat(2).SetPrec(prec) 25 | sqrtTwo := new(big.Float).SetPrec(prec).Sqrt(two) 26 | 27 | // Pow uses the first argument's precision. 28 | gsc := bigfloat.Pow(two, sqrtTwo) // 2^√2 29 | fmt.Printf("gsc = %.60f\n", gsc) 30 | } 31 | ``` 32 | 33 | outputs: 34 | ``` 35 | gsc = 2.665144142690225188650297249873139848274211313714659492835980 36 | ``` 37 | 38 | #### Documentation 39 | 40 | See https://godoc.org/github.com/ALTree/bigfloat 41 | -------------------------------------------------------------------------------- /exp.go: -------------------------------------------------------------------------------- 1 | package bigfloat 2 | 3 | import ( 4 | "math" 5 | "math/big" 6 | ) 7 | 8 | // Exp returns a big.Float representation of exp(z). Precision is 9 | // the same as the one of the argument. The function returns +Inf 10 | // when z = +Inf, and 0 when z = -Inf. 11 | func Exp(z *big.Float) *big.Float { 12 | 13 | // exp(0) == 1 14 | if z.Sign() == 0 { 15 | return big.NewFloat(1).SetPrec(z.Prec()) 16 | } 17 | 18 | // Exp(+Inf) = +Inf 19 | if z.IsInf() && z.Sign() > 0 { 20 | return big.NewFloat(math.Inf(+1)).SetPrec(z.Prec()) 21 | } 22 | 23 | // Exp(-Inf) = 0 24 | if z.IsInf() && z.Sign() < 0 { 25 | return big.NewFloat(0).SetPrec(z.Prec()) 26 | } 27 | 28 | guess := new(big.Float) 29 | 30 | // try to get initial estimate using IEEE-754 math 31 | zf, _ := z.Float64() 32 | if zfs := math.Exp(zf); zfs == math.Inf(+1) || zfs == 0 { 33 | // too big or too small for IEEE-754 math, 34 | // perform argument reduction using 35 | // e^{2z} = (e^z)² 36 | halfZ := new(big.Float).Mul(z, big.NewFloat(0.5)) 37 | halfExp := Exp(halfZ.SetPrec(z.Prec() + 64)) 38 | return new(big.Float).Mul(halfExp, halfExp).SetPrec(z.Prec()) 39 | } else { 40 | // we got a nice IEEE-754 estimate 41 | guess.SetFloat64(zfs) 42 | } 43 | 44 | // f(t)/f'(t) = t*(log(t) - z) 45 | f := func(t *big.Float) *big.Float { 46 | x := new(big.Float) 47 | x.Sub(Log(t), z) 48 | return x.Mul(x, t) 49 | } 50 | 51 | x := newton(f, guess, z.Prec()) 52 | 53 | return x 54 | } 55 | -------------------------------------------------------------------------------- /exp_test.go: -------------------------------------------------------------------------------- 1 | package bigfloat_test 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/big" 7 | "math/rand" 8 | "testing" 9 | 10 | "github.com/ALTree/bigfloat" 11 | ) 12 | 13 | func TestExp(t *testing.T) { 14 | for _, test := range []struct { 15 | z string 16 | want string 17 | }{ 18 | {"0", "1"}, 19 | {"1", "2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190115738341879307021540891499348841675092447614606680822648001684774118537423454424371075390777449920695517027618386062613313845830007520449338265602976"}, 20 | {"1.5", "4.4816890703380648226020554601192758190057498683696670567726500827859366744667137729810538313824533913886163506518301957689627464772204086069617596449736935381785298966216866555101351015468252930697652168582207145843214628870201383138594206299440366192845735003904511744769330306157776861818063974846024442278949433305735015582659896397844432731673273"}, 21 | {"2", "7.3890560989306502272304274605750078131803155705518473240871278225225737960790577633843124850791217947737531612654788661238846036927812733744783922133980777749001228956074107537023913309475506820865818202696478682084042209822552348757424625414146799281293318880707633010193378997407299869600953033075153208188236846947930299135587714456831239232727646"}, 22 | {"3", "20.085536923187667740928529654581717896987907838554150144378934229698845878091973731204497160253017702153607615851949002881811012479353506690232621784477250503945677100066077851812229047884383940258152534709352622981465538424555697733515108150118404754933838497843177676070913772862491787349396037822793717687131254060597553426640826030948663920216259"}, 23 | 24 | {"-1", "0.36787944117144232159552377016146086744581113103176783450783680169746149574489980335714727434591964374662732527684399520824697579279012900862665358949409878309219436737733811504863899112514561634498771997868447595793974730254989249545323936620796481051464752061229422308916492656660036507457728370553285373838810680478761195682989345449735073931859922"}, 25 | {"-2", "0.13533528323661269189399949497248440340763154590957588146815887265407337410148768993709812249065704875507728718963355221244934687189285303815889513499670600559125022755868258230483842057584538468003599408344602481287135375015664353399593608501390049529421705857601948571122397095990883595090571764528251279379538022237440385068269131295459365886804367"}, 26 | {"-3", "0.049787068367863942979342415650061776631699592188423215567627727606060667730199550154054244236633344526401328650893681950864643386736174297123488422626590132549710257089250891729183705544267766471294627261313755158051249249208013335774449487985072339959233419058693861230319791977014791562486437888837044087835498513120050395020976909328170160274676519"}, 27 | 28 | {"10", "22026.465794806716516957900645284244366353512618556781074235426355225202818570792575199120968164525895451555501092457836652423291606522895166222480137728972873485577837847275195480610095881417055888657927317236168401192698035170264925041101757502556764762696107543817931960834044404934236682455357614946828619042431465132389556031319229262768101604495"}, 29 | {"100", "2.6881171418161354484126255515800135873611118773741922415191608615280287034909564914158871097219845710811670879190576068697597709761868233548459638929871966089629133626120029380957276534032962269865668016917743514451846065162804442237756762296960284731911402129862281040057911593878790384974173340084912432828126815454426051808828625966509400466909062e43"}, 30 | {"1000", "1.9700711140170469938888793522433231253169379853238457899528029913850638507824411934749780765630268899309638179875202269359829817305446128992326278366015282523232053516958456675619227156760278807142246682631400685516850865349794166031604536781793809290529972858013286994585647028653437590045656435558915622042232026051882611228863835837224872472521451e434"}, 31 | 32 | {"-10", "0.000045399929762484851535591515560550610237918088866564969259071305650999421614302281652525004545947782321708055089686028492945199117244520388837183347709414567560990909217007363970181059501783900762968517787030908824365171548448722293652332416020501168264360305604941570107729975354408079403994232932138270780520042710498960354486166066837009201707573209"}, 33 | {"-100", "3.7200759760208359629596958038631183373588922923767819671206138766632904758958157181571187786422814966019356176423110698002479856420525356002661856882839075574388191160228448691497585855102816611741608772370701345082175755257496876380478927279529400619796226477050521097935092405571614981699373980650794385017392666116669084820355852767349264735965334e-44"}, 34 | {"-1000", "5.0759588975494567652918094795743369193055992828928373618323938454105405429748191756796621690465428678636671068310652851135787934480190632251259072300213915638091771495398351108574919194309548129952421441572726108465407163812260104924530270737073247546217081943180823516857873407345613076984468096760005536701904004361380296144254899617340297251706670e-435"}, 35 | } { 36 | for _, prec := range []uint{24, 53, 64, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} { 37 | want := new(big.Float).SetPrec(prec) 38 | want.Parse(test.want, 10) 39 | 40 | z := new(big.Float).SetPrec(prec) 41 | z.Parse(test.z, 10) 42 | 43 | x := bigfloat.Exp(z) 44 | 45 | if x.Cmp(want) != 0 { 46 | t.Errorf("prec = %d, Exp(%v) =\ngot %g;\nwant %g", prec, test.z, x, want) 47 | } 48 | } 49 | } 50 | } 51 | 52 | func testExpFloat64(scale float64, nTests int, t *testing.T) { 53 | for i := 0; i < nTests; i++ { 54 | r := rand.Float64() * scale 55 | 56 | z := big.NewFloat(r) 57 | z64, acc := bigfloat.Exp(z).Float64() 58 | 59 | want := math.Exp(r) 60 | 61 | // Unfortunately, the Go math.Exp function is not completely 62 | // accurate, so it doesn't make sense to require 100% 63 | // compatibility with it, since it happens that math.Exp 64 | // returns a result with the last bit off (same as math.Log). 65 | // 66 | // Just require a relative error smaller than 1e-14. 67 | if math.Abs(z64-want)/want > 1e-14 || acc != big.Exact { 68 | t.Errorf("Exp(%g) =\n got %g (%s);\nwant %g (Exact)", z, z64, acc, want) 69 | } 70 | } 71 | } 72 | 73 | func TestExpFloat64Small(t *testing.T) { 74 | testExpFloat64(-100, 4e3, t) 75 | testExpFloat64(-10, 4e3, t) 76 | testExpFloat64(-1, 4e3, t) 77 | } 78 | 79 | func TestExpFloat64Medium(t *testing.T) { 80 | testExpFloat64(0.1, 5e3, t) 81 | testExpFloat64(1, 5e3, t) 82 | } 83 | 84 | func TestExpFloat64Big(t *testing.T) { 85 | testExpFloat64(10, 5e3, t) 86 | testExpFloat64(100, 5e3, t) 87 | } 88 | 89 | func TestExpSpecialValues(t *testing.T) { 90 | for _, f := range []float64{ 91 | +0.0, 92 | -0.0, 93 | math.Inf(+1), 94 | math.Inf(-1), 95 | } { 96 | z := big.NewFloat(f) 97 | x64, acc := bigfloat.Exp(z).Float64() 98 | want := math.Exp(f) 99 | if x64 != want || acc != big.Exact { 100 | t.Errorf("Log(%f) =\n got %g (%s);\nwant %g (Exact)", f, x64, acc, want) 101 | } 102 | } 103 | } 104 | 105 | // ---------- Benchmarks ---------- 106 | 107 | func BenchmarkExp(b *testing.B) { 108 | z := big.NewFloat(2).SetPrec(1e5) 109 | _ = bigfloat.Exp(z) // fill pi cache before benchmarking 110 | 111 | for _, prec := range []uint{1e2, 1e3, 1e4, 1e5} { 112 | z = big.NewFloat(2).SetPrec(prec) 113 | b.Run(fmt.Sprintf("%v", prec), func(b *testing.B) { 114 | b.ReportAllocs() 115 | for n := 0; n < b.N; n++ { 116 | bigfloat.Exp(z) 117 | } 118 | }) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ALTree/bigfloat 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /log.go: -------------------------------------------------------------------------------- 1 | package bigfloat 2 | 3 | import ( 4 | "math" 5 | "math/big" 6 | ) 7 | 8 | // Log returns a big.Float representation of the natural logarithm of 9 | // z. Precision is the same as the one of the argument. The function 10 | // panics if z is negative, returns -Inf when z = 0, and +Inf when z = 11 | // +Inf 12 | func Log(z *big.Float) *big.Float { 13 | 14 | // panic on negative z 15 | if z.Sign() == -1 { 16 | panic("Log: argument is negative") 17 | } 18 | 19 | // Log(0) = -Inf 20 | if z.Sign() == 0 { 21 | return big.NewFloat(math.Inf(-1)).SetPrec(z.Prec()) 22 | } 23 | 24 | prec := z.Prec() + 64 // guard digits 25 | 26 | one := big.NewFloat(1).SetPrec(prec) 27 | two := big.NewFloat(2).SetPrec(prec) 28 | four := big.NewFloat(4).SetPrec(prec) 29 | 30 | // Log(1) = 0 31 | if z.Cmp(one) == 0 { 32 | return big.NewFloat(0).SetPrec(z.Prec()) 33 | } 34 | 35 | // Log(+Inf) = +Inf 36 | if z.IsInf() { 37 | return big.NewFloat(math.Inf(+1)).SetPrec(z.Prec()) 38 | } 39 | 40 | x := new(big.Float).SetPrec(prec) 41 | 42 | // if 0 < z < 1 we compute log(z) as -log(1/z) 43 | var neg bool 44 | if z.Cmp(one) < 0 { 45 | x.Quo(one, z) 46 | neg = true 47 | } else { 48 | x.Set(z) 49 | } 50 | 51 | // We scale up x until x >= 2**(prec/2), and then we'll be allowed 52 | // to use the AGM formula for Log(x). 53 | // 54 | // Double x until the condition is met, and keep track of the 55 | // number of doubling we did (needed to scale back later). 56 | 57 | lim := new(big.Float) 58 | lim.SetMantExp(two, int(prec/2)) 59 | 60 | k := 0 61 | for x.Cmp(lim) < 0 { 62 | x.Mul(x, x) 63 | k++ 64 | } 65 | 66 | // Compute the natural log of x using the fact that 67 | // log(x) = π / (2 * AGM(1, 4/x)) 68 | // if 69 | // x >= 2**(prec/2), 70 | // where prec is the desired precision (in bits) 71 | pi := pi(prec) 72 | agm := agm(one, x.Quo(four, x)) // agm = AGM(1, 4/x) 73 | 74 | x.Quo(pi, x.Mul(two, agm)) // reuse x, we don't need it 75 | 76 | if neg { 77 | x.Neg(x) 78 | } 79 | 80 | // scale the result back multiplying by 2**-k 81 | // reuse lim to reduce allocations. 82 | x.Mul(x, lim.SetMantExp(one, -k)) 83 | 84 | return x.SetPrec(z.Prec()) 85 | } 86 | -------------------------------------------------------------------------------- /log_test.go: -------------------------------------------------------------------------------- 1 | package bigfloat_test 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/big" 7 | "math/rand" 8 | "testing" 9 | 10 | "github.com/ALTree/bigfloat" 11 | ) 12 | 13 | // See note in sqrt_test.go about which numbers 14 | // can we safely test this way. 15 | 16 | func TestLog(t *testing.T) { 17 | for _, test := range []struct { 18 | z string 19 | want string 20 | }{ 21 | // 350 decimal digits are enough to give us up to 1000 binary digits 22 | {"0.5", "-0.69314718055994530941723212145817656807550013436025525412068000949339362196969471560586332699641868754200148102057068573368552023575813055703267075163507596193072757082837143519030703862389167347112335011536449795523912047517268157493206515552473413952588295045300709532636664265410423915781495204374043038550080194417064167151864471283996817178454696"}, 23 | {"0.25", "-1.3862943611198906188344642429163531361510002687205105082413600189867872439393894312117266539928373750840029620411413714673710404715162611140653415032701519238614551416567428703806140772477833469422467002307289959104782409503453631498641303110494682790517659009060141906527332853082084783156299040874808607710016038883412833430372894256799363435690939"}, 24 | {"0.0125", "-4.3820266346738816122696878190588939118276018917095387383953679294477534755864366270535871860788543609679722271039983058344660861723571984277642996240040095752750899208106689864147210106979082189417635556550588715983462075888842670124944153533207460860520530946333864410280342429017041970928492563533263928706772062013203262792640026952942261381891629"}, 25 | 26 | {"1", "0.0"}, 27 | {"2", "0.69314718055994530941723212145817656807550013436025525412068000949339362196969471560586332699641868754200148102057068573368552023575813055703267075163507596193072757082837143519030703862389167347112335011536449795523912047517268157493206515552473413952588295045300709532636664265410423915781495204374043038550080194417064167151864471283996817178454696"}, 28 | {"10", "2.3025850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983419677840422862486334095254650828067566662873690987816894829072083255546808437998948262331985283935053089653777326288461633662222876982198867465436674744042432743651550489343149393914796194044002221051017141748003688084012647080685567743216228355220"}, 29 | {"4096", "8.3177661667193437130067854574981188169060016123230630494481601139207234636363365872703599239570242505040177722468482288042262428290975666843920490196209115431687308499404572222836844634867000816534802013843739754628694457020721788991847818662968096743105954054360851439163997118492508698937794245248851646260096233300477000582237365540796180614145634"}, 30 | {"1e5", "11.512925464970228420089957273421821038005507443143864880166639504837863048386762401179986025447991491709838920211431243167047627325414033783331436845493908447414536041627773404218999474131165992641967526544826888663144230816831111438491099433732718337372021216371825775244671574696957398097022001110525508570874001844042006323540342783871608114177610"}, 31 | } { 32 | for _, prec := range []uint{24, 53, 64, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} { 33 | want := new(big.Float).SetPrec(prec) 34 | want.Parse(test.want, 10) 35 | 36 | z := new(big.Float).SetPrec(prec) 37 | z.Parse(test.z, 10) 38 | 39 | x := bigfloat.Log(z) 40 | 41 | if x.Cmp(want) != 0 { 42 | t.Errorf("prec = %d, Log(%v) =\ngot %g;\n want %g", prec, test.z, x, want) 43 | } 44 | } 45 | } 46 | } 47 | 48 | func testLogFloat64(scale float64, nTests int, t *testing.T) { 49 | for i := 0; i < nTests; i++ { 50 | r := rand.Float64() * scale 51 | 52 | z := big.NewFloat(r) 53 | x64, acc := bigfloat.Log(z).Float64() 54 | 55 | want := math.Log(r) 56 | 57 | // Unfortunately, the Go math.Log function is not completely 58 | // accurate, so it doesn't make sense to require 100% 59 | // compatibility with it, since it happens that math.Log 60 | // returns a result with the last bit off (see Issue #9546). 61 | // 62 | // Just require a relative error smaller than 1e-14. 63 | if math.Abs(x64-want)/want > 1e-14 || acc != big.Exact { 64 | t.Errorf("Log(%g) =\n got %g (%s);\nwant %g (Exact)", z, x64, acc, want) 65 | } 66 | } 67 | } 68 | 69 | func TestLogFloat64Small(t *testing.T) { 70 | testLogFloat64(1e-100, 1e4, t) 71 | testLogFloat64(1e-10, 1e4, t) 72 | } 73 | 74 | func TestLogFloat64Medium(t *testing.T) { 75 | testLogFloat64(1, 1e4, t) 76 | testLogFloat64(100, 1e4, t) 77 | } 78 | 79 | func TestLogFloat64Big(t *testing.T) { 80 | testLogFloat64(1e10, 1e4, t) 81 | testLogFloat64(1e100, 1e4, t) 82 | } 83 | 84 | func TestLogSpecialValues(t *testing.T) { 85 | for _, f := range []float64{ 86 | +0.0, 87 | -0.0, 88 | math.Inf(+1), 89 | } { 90 | z := big.NewFloat(f) 91 | x64, acc := bigfloat.Log(z).Float64() 92 | want := math.Log(f) 93 | if x64 != want || acc != big.Exact { 94 | t.Errorf("Log(%f) =\n got %g (%s);\nwant %g (Exact)", f, x64, acc, want) 95 | } 96 | } 97 | } 98 | 99 | // ---------- Benchmarks ---------- 100 | 101 | func BenchmarkLog(b *testing.B) { 102 | z := big.NewFloat(2).SetPrec(1e5) 103 | _ = bigfloat.Log(z) // fill pi cache before benchmarking 104 | 105 | for _, prec := range []uint{1e2, 1e3, 1e4, 1e5} { 106 | z = big.NewFloat(2).SetPrec(prec) 107 | b.Run(fmt.Sprintf("%v", prec), func(b *testing.B) { 108 | b.ReportAllocs() 109 | for n := 0; n < b.N; n++ { 110 | bigfloat.Log(z) 111 | } 112 | }) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /misc.go: -------------------------------------------------------------------------------- 1 | package bigfloat 2 | 3 | import "math/big" 4 | 5 | // agm returns the arithmetic-geometric mean of a and b. 6 | // a and b must have the same precision. 7 | func agm(a, b *big.Float) *big.Float { 8 | 9 | if a.Prec() != b.Prec() { 10 | panic("agm: different precisions") 11 | } 12 | 13 | prec := a.Prec() 14 | 15 | // do not overwrite a and b 16 | a2 := new(big.Float).Copy(a).SetPrec(prec + 64) 17 | b2 := new(big.Float).Copy(b).SetPrec(prec + 64) 18 | 19 | if a2.Cmp(b2) == -1 { 20 | a2, b2 = b2, a2 21 | } 22 | // a2 >= b2 23 | 24 | // set lim to 2**(-prec) 25 | lim := new(big.Float) 26 | lim.SetMantExp(big.NewFloat(1).SetPrec(prec+64), -int(prec+1)) 27 | 28 | half := big.NewFloat(0.5) 29 | t := new(big.Float) 30 | 31 | for t.Sub(a2, b2).Cmp(lim) != -1 { 32 | t.Copy(a2) 33 | a2.Add(a2, b2).Mul(a2, half) 34 | b2.Sqrt(b2.Mul(b2, t)) 35 | } 36 | 37 | return a2.SetPrec(prec) 38 | } 39 | 40 | var piCache *big.Float 41 | var piCachePrec uint 42 | var enablePiCache bool = true 43 | 44 | func init() { 45 | if !enablePiCache { 46 | return 47 | } 48 | 49 | piCache, _, _ = new(big.Float).SetPrec(1024).Parse("3."+ 50 | "14159265358979323846264338327950288419716939937510"+ 51 | "58209749445923078164062862089986280348253421170679"+ 52 | "82148086513282306647093844609550582231725359408128"+ 53 | "48111745028410270193852110555964462294895493038196"+ 54 | "44288109756659334461284756482337867831652712019091"+ 55 | "45648566923460348610454326648213393607260249141273"+ 56 | "72458700660631558817488152092096282925409171536444", 10) 57 | 58 | piCachePrec = 1024 59 | } 60 | 61 | // pi returns pi to prec bits of precision 62 | func pi(prec uint) *big.Float { 63 | 64 | if prec <= piCachePrec && enablePiCache { 65 | return new(big.Float).Copy(piCache).SetPrec(prec) 66 | } 67 | 68 | // Following R. P. Brent, Multiple-precision zero-finding 69 | // methods and the complexity of elementary function evaluation, 70 | // in Analytic Computational Complexity, Academic Press, 71 | // New York, 1975, Section 8. 72 | 73 | half := big.NewFloat(0.5) 74 | two := big.NewFloat(2).SetPrec(prec + 64) 75 | sqrt2 := new(big.Float).SetPrec(prec + 64).Sqrt(two) 76 | 77 | a := big.NewFloat(1).SetPrec(prec + 64) // a = 1 78 | b := new(big.Float).SetPrec(prec+64).Mul(sqrt2, half) // b = 1/√2 79 | t := big.NewFloat(0.25).SetPrec(prec + 64) // t = 1/4 80 | x := big.NewFloat(1).SetPrec(prec + 64) // x = 1 81 | 82 | // limit is 2**(-prec) 83 | lim := new(big.Float) 84 | lim.SetMantExp(big.NewFloat(1).SetPrec(prec+64), -int(prec+1)) 85 | 86 | // temp variables 87 | y := new(big.Float) 88 | for y.Sub(a, b).Cmp(lim) != -1 { // assume a > b 89 | y.Copy(a) 90 | a.Add(a, b).Mul(a, half) // a = (a+b)/2 91 | b.Sqrt(b.Mul(b, y)) // b = √(ab) 92 | 93 | y.Sub(a, y) // y = a - y 94 | y.Mul(y, y).Mul(y, x) // y = x(a-y)² 95 | t.Sub(t, y) // t = t - x(a-y)² 96 | x.Mul(x, two) // x = 2x 97 | } 98 | 99 | a.Mul(a, a).Quo(a, t) // π = a² / t 100 | a.SetPrec(prec) 101 | 102 | if enablePiCache { 103 | piCache.Copy(a) 104 | piCachePrec = prec 105 | } 106 | 107 | return a 108 | } 109 | 110 | // returns an approximate (to precision dPrec) solution to 111 | // f(t) = 0 112 | // using the Newton Method. 113 | // fOverDf needs to be a fuction returning f(t)/f'(t). 114 | // t must not be changed by fOverDf. 115 | // guess is the initial guess (and it's not preserved). 116 | func newton(fOverDf func(z *big.Float) *big.Float, guess *big.Float, dPrec uint) *big.Float { 117 | 118 | prec, guard := guess.Prec(), uint(64) 119 | guess.SetPrec(prec + guard) 120 | 121 | for prec < 2*dPrec { 122 | guess.Sub(guess, fOverDf(guess)) 123 | prec *= 2 124 | guess.SetPrec(prec + guard) 125 | } 126 | 127 | return guess.SetPrec(dPrec) 128 | } 129 | -------------------------------------------------------------------------------- /misc_test.go: -------------------------------------------------------------------------------- 1 | package bigfloat 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "testing" 7 | ) 8 | 9 | const maxPrec uint = 1100 10 | 11 | func TestAgm(t *testing.T) { 12 | for _, test := range []struct { 13 | a, b string 14 | want string 15 | }{ 16 | // 350 decimal digits are enough to give us up to 1000 binary digits 17 | {"1", "2", "1.4567910310469068691864323832650819749738639432213055907941723832679264545802509002574737128184484443281894018160367999355762430743401245116912132499522793768970211976726893728266666782707432902072384564600963133367494416649516400826932239086263376738382410254887262645136590660408875885100466728130947439789355129117201754471869564160356411130706061"}, 18 | {"1", "10", "4.2504070949322748617281643183731348667984678641901928596701476622237553127409037845252854607876171790458817135897668652366410690187825866854343005714304399718866701345600268795095037823053677248108795697049522041225723229732458947507697835936406527028150257238518982793084569470658500853106997941082919334694146843915361847332301248942222685517896377"}, 19 | {"1", "0.125", "0.45196952219967034359164911331276507645541557018306954112635037493237190371123433961098897571407153216488726488616781446636283304514042965741376539315003644325377859387794608118242990700589889155408232061013871480906595147189700268152276449512798584772002737950386745259435790965051247641106770187776231088478906739003673011639874297764052324720923824"}, 20 | {"1", "0.00390625", "0.2266172673264813935990249059047521131153183423554951008357647589399579243281007098800682366778894106068183449922373565084840603788091294841822891406755449218057751291845474188560350241555526734834267320629182988862200822134426714354129001630331838172767684623648755579758508073234772093745831056731263684472818466567279847347734121500617411676068370"}, 21 | {"1", "0.0001220703125", "0.15107867088555894565277006051956059212554039802503247524478909254186086852737399490629222674071181480492157167137547694132610166031526264375084434300568336411139925857454913414480542768807718797335060713475211709310835676172131569048902323084439330888400622327072954342544508199547787750415198261456314278054748992781108231991187512975110547417178045"}, 22 | } { 23 | for _, prec := range []uint{24, 53, 64, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} { 24 | want := new(big.Float).SetPrec(prec) 25 | want.Parse(test.want, 10) 26 | 27 | a := new(big.Float).SetPrec(prec) 28 | a.Parse(test.a, 10) 29 | 30 | b := new(big.Float).SetPrec(prec) 31 | b.Parse(test.b, 10) 32 | 33 | z := agm(a, b) 34 | 35 | if z.Cmp(want) != 0 { 36 | t.Errorf("prec = %d, Agm(%v, %v) =\ngot %g;\nwant %g", prec, test.a, test.b, z, want) 37 | } 38 | } 39 | } 40 | } 41 | 42 | func TestPi(t *testing.T) { 43 | enablePiCache = false 44 | piStr := "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153644" 45 | for _, prec := range []uint{24, 53, 64, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} { 46 | 47 | want := new(big.Float).SetPrec(prec) 48 | want.Parse(piStr, 10) 49 | 50 | z := pi(prec) 51 | 52 | if z.Cmp(want) != 0 { 53 | t.Errorf("Pi(%d) =\ngot %g;\nwant %g", prec, z, want) 54 | } 55 | } 56 | enablePiCache = true 57 | } 58 | 59 | // ---------- Benchmarks ---------- 60 | 61 | func BenchmarkAgm(b *testing.B) { 62 | for _, prec := range []uint{1e2, 1e3, 1e4, 1e5} { 63 | x := new(big.Float).SetPrec(prec).SetFloat64(1) 64 | y := new(big.Float).SetPrec(prec).SetFloat64(0.125) 65 | b.Run(fmt.Sprintf("%v", prec), func(b *testing.B) { 66 | b.ReportAllocs() 67 | for n := 0; n < b.N; n++ { 68 | agm(x, y) 69 | } 70 | }) 71 | } 72 | } 73 | 74 | func BenchmarkPi(b *testing.B) { 75 | enablePiCache = false 76 | for _, prec := range []uint{1e2, 1e3, 1e4, 1e5} { 77 | b.Run(fmt.Sprintf("%v", prec), func(b *testing.B) { 78 | b.ReportAllocs() 79 | for n := 0; n < b.N; n++ { 80 | pi(prec) 81 | } 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /pow.go: -------------------------------------------------------------------------------- 1 | package bigfloat 2 | 3 | import "math/big" 4 | 5 | // Pow returns a big.Float representation of z**w. Precision is the same as the one 6 | // of the first argument. The function panics when z is negative. 7 | func Pow(z *big.Float, w *big.Float) *big.Float { 8 | 9 | if z.Sign() < 0 { 10 | panic("Pow: negative base") 11 | } 12 | 13 | // Pow(z, 0) = 1.0 14 | if w.Sign() == 0 { 15 | return big.NewFloat(1).SetPrec(z.Prec()) 16 | } 17 | 18 | // Pow(z, 1) = z 19 | // Pow(+Inf, n) = +Inf 20 | if w.Cmp(big.NewFloat(1)) == 0 || z.IsInf() { 21 | return new(big.Float).Copy(z) 22 | } 23 | 24 | // Pow(z, -w) = 1 / Pow(z, w) 25 | if w.Sign() < 0 { 26 | x := new(big.Float) 27 | zExt := new(big.Float).Copy(z).SetPrec(z.Prec() + 64) 28 | wNeg := new(big.Float).Neg(w) 29 | return x.Quo(big.NewFloat(1), Pow(zExt, wNeg)).SetPrec(z.Prec()) 30 | } 31 | 32 | // w integer fast path (disabled because introduces rounding 33 | // errors) 34 | if false && w.IsInt() { 35 | wi, _ := w.Int64() 36 | return powInt(z, int(wi)) 37 | } 38 | 39 | // compute w**z as exp(z log(w)) 40 | x := new(big.Float).SetPrec(z.Prec() + 64) 41 | logZ := Log(new(big.Float).Copy(z).SetPrec(z.Prec() + 64)) 42 | x.Mul(w, logZ) 43 | x = Exp(x) 44 | return x.SetPrec(z.Prec()) 45 | 46 | } 47 | 48 | // fast path for z**w when w is an integer 49 | func powInt(z *big.Float, w int) *big.Float { 50 | 51 | // get mantissa and exponent of z 52 | mant := new(big.Float) 53 | exp := z.MantExp(mant) 54 | 55 | // result's exponent 56 | exp = exp * w 57 | 58 | // result's mantissa 59 | x := big.NewFloat(1).SetPrec(z.Prec()) 60 | 61 | // Classic right-to-left binary exponentiation 62 | for w > 0 { 63 | if w%2 == 1 { 64 | x.Mul(x, mant) 65 | } 66 | w >>= 1 67 | mant.Mul(mant, mant) 68 | } 69 | 70 | return new(big.Float).SetMantExp(x, exp) 71 | } 72 | -------------------------------------------------------------------------------- /pow_test.go: -------------------------------------------------------------------------------- 1 | package bigfloat_test 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/big" 7 | "math/rand" 8 | "testing" 9 | 10 | "github.com/ALTree/bigfloat" 11 | ) 12 | 13 | func TestPow(t *testing.T) { 14 | for _, test := range []struct { 15 | z, w string 16 | want string 17 | }{ 18 | {"1.5", "1.5", "1.8371173070873835736479630560294185439744606104925025963245194254382202830929862699048945748284801761139459509199606418436441490948783180062193379634279589146216845606457574284357225789531838276676109830092400181402243325144092030253566067045309391758849310432709781082027026621306513787250611923558785098172755465204952231278685708006003328040156619"}, 19 | {"2", "1.5", "2.8284271247461900976033774484193961571393437507538961463533594759814649569242140777007750686552831454700276924618245940498496721117014744252882429941998716628264453318550111855115999010023055641211429402191199432119405490691937240294570348372817783972191046584609686174286429016795252072559905028159793745067930926636176592812412305167047901094915006"}, 20 | 21 | {"1.5", "-1.5", "0.54433105395181735515495201660130919821465499570148225076282057050021341721273667256441320735658671884857657805035870869441308121329727940925017421138606190062864727722837257138836224561575817116077362459533037574525165407834346756306862420874990790396590549430251203206006004803871151962224035329063066957548905082088747351936846542240009860859723315"}, 22 | {"2", "-1.5", "0.35355339059327376220042218105242451964241796884423701829416993449768311961552675971259688358191039318375346155772807425623120901396268430316103037427498395785330566648187639818894998762528819551514286752738999290149256863364921550368212935466022229965238808230762107717858036270994065090699881285199742181334913658295220741015515381458809876368643757"}, 23 | } { 24 | for _, prec := range []uint{24, 53, 64, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} { 25 | want := new(big.Float).SetPrec(prec) 26 | want.Parse(test.want, 10) 27 | 28 | z := new(big.Float).SetPrec(prec) 29 | z.Parse(test.z, 10) 30 | w := new(big.Float).SetPrec(prec) 31 | w.Parse(test.w, 10) 32 | 33 | x := bigfloat.Pow(z, w) 34 | 35 | if x.Cmp(want) != 0 { 36 | t.Errorf("prec = %d, Pow(%v, %v) =\ngot %g;\nwant %g", prec, test.z, test.w, x, want) 37 | } 38 | } 39 | } 40 | } 41 | 42 | func TestPowIntegers(t *testing.T) { 43 | for _, test := range []struct { 44 | z, w string 45 | want string 46 | }{ 47 | {"2", "5", "32"}, 48 | {"2", "10", "1024"}, 49 | {"2", "64", "18446744073709551616"}, 50 | 51 | {"2", "-5", "0.03125"}, 52 | {"2", "-10", "0.0009765625"}, 53 | {"2", "-64", "5.42101086242752217003726400434970855712890625e-20"}, 54 | 55 | {"1.5", "8", "25.62890625"}, 56 | } { 57 | for _, prec := range []uint{24, 53, 64, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} { 58 | want := new(big.Float).SetPrec(prec) 59 | want.Parse(test.want, 10) 60 | 61 | z := new(big.Float).SetPrec(prec) 62 | z.Parse(test.z, 10) 63 | w := new(big.Float).SetPrec(prec) 64 | w.Parse(test.w, 10) 65 | 66 | x := bigfloat.Pow(z, w) 67 | 68 | if x.Cmp(want) != 0 { 69 | t.Errorf("prec = %d, Pow(%v, %v) =\ngot %g;\nwant %g", prec, test.z, test.w, x, want) 70 | } 71 | } 72 | } 73 | } 74 | 75 | func testPowFloat64(scale float64, nTests int, t *testing.T) { 76 | for i := 0; i < nTests; i++ { 77 | r1 := math.Abs(rand.Float64() * scale) // base always > 0 78 | r2 := rand.Float64() * scale 79 | 80 | z := big.NewFloat(r1).SetPrec(53) 81 | w := big.NewFloat(r2).SetPrec(53) 82 | 83 | x64, acc := bigfloat.Pow(z, w).Float64() 84 | 85 | want := math.Pow(r1, r2) 86 | 87 | // Unfortunately, the Go math.Pow function is not completely 88 | // accurate, so it doesn't make sense to require 100% 89 | // compatibility with it, since it happens that math.Pow 90 | // returns a result with the last bit off (same as math.Log). 91 | // 92 | // Just require a relative error smaller than 1e-14. 93 | if math.Abs(x64-want)/want > 1e-14 || acc != big.Exact { 94 | t.Errorf("Pow(%g, %g) =\n got %g (%s);\nwant %g (Exact)", z, w, x64, acc, want) 95 | } 96 | } 97 | } 98 | 99 | func TestPowFloat64Small(t *testing.T) { 100 | testPowFloat64(-100, 1e3, t) 101 | testPowFloat64(-10, 1e3, t) 102 | testPowFloat64(-1, 1e3, t) 103 | } 104 | 105 | func TestPowFloat64Medium(t *testing.T) { 106 | testPowFloat64(0.1, 4e3, t) 107 | testPowFloat64(1, 4e3, t) 108 | } 109 | 110 | func TestPowFloat64Big(t *testing.T) { 111 | testPowFloat64(10, 4e3, t) 112 | testPowFloat64(100, 4e3, t) 113 | } 114 | 115 | func TestPowSpecialValues(t *testing.T) { 116 | for _, f := range []struct { 117 | z, w float64 118 | }{ 119 | {2, +0.0}, 120 | {2, -0.0}, 121 | {4.2, 1.0}, 122 | {math.Inf(+1), 2.0}, 123 | } { 124 | z := big.NewFloat(f.z).SetPrec(53) 125 | w := big.NewFloat(f.w).SetPrec(53) 126 | x64, acc := bigfloat.Pow(z, w).Float64() 127 | want := math.Pow(f.z, f.w) 128 | if x64 != want || acc != big.Exact { 129 | t.Errorf("Pow(%g, %g) =\n got %g (%s);\nwant %g (Exact)", f.z, f.w, x64, acc, want) 130 | } 131 | } 132 | } 133 | 134 | // ---------- Benchmarks ---------- 135 | 136 | func BenchmarkPowInt(b *testing.B) { 137 | z := big.NewFloat(2).SetPrec(1e5) 138 | w := big.NewFloat(50).SetPrec(1e5) 139 | _ = bigfloat.Pow(z, w) // fill pi cache before benchmarking 140 | 141 | for _, prec := range []uint{1e2, 1e3, 1e4, 1e5} { 142 | z = big.NewFloat(2).SetPrec(prec) 143 | w = big.NewFloat(50).SetPrec(prec) 144 | b.Run(fmt.Sprintf("%v", prec), func(b *testing.B) { 145 | b.ReportAllocs() 146 | for n := 0; n < b.N; n++ { 147 | bigfloat.Pow(z, w) 148 | } 149 | }) 150 | } 151 | } 152 | 153 | func BenchmarkPow(b *testing.B) { 154 | z := big.NewFloat(2).SetPrec(1e5) 155 | w := big.NewFloat(1.5).SetPrec(1e5) 156 | _ = bigfloat.Pow(z, w) // fill pi cache before benchmarking 157 | 158 | for _, prec := range []uint{1e2, 1e3, 1e4, 1e5} { 159 | z = big.NewFloat(2).SetPrec(prec) 160 | w = big.NewFloat(1.5).SetPrec(prec) 161 | b.Run(fmt.Sprintf("%v", prec), func(b *testing.B) { 162 | b.ReportAllocs() 163 | for n := 0; n < b.N; n++ { 164 | bigfloat.Pow(z, w) 165 | } 166 | }) 167 | } 168 | } 169 | --------------------------------------------------------------------------------