printfn / fend

Arbitrary-precision unit-aware calculator

Home Page:https://printfn.github.io/fend

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

exp(x) or e^x uses large amounts of CPU and stalls on certain fractional values of x

Nicbudd opened this issue · comments

Reproduce:
> e^(27) - Finishes immediately
> e^(27.5) - Finishes immediately
> e^(27.2) - Takes ~0.5s
> e^(27.02) - Stalls for a very long time, CPU usage jumps to 100% for a single core.

This is what I was trying to calculate when I encountered this error:

> i_c = 1mA
1 mA
> V_BE = 700 mV
700 mV
> V_T = 25.9mV
25.9 mV
> I_S = i_c/(e^(V_BE/V_T))

FWIW, Rust handles this calculation just fine:

fn main() {
        println!("{}", (7000f64/259f64).exp());
}

This program runs in under 2ms on my machine.

That's caused by fend's use of arbitrary-precision rational numbers, whereas Rust just uses IEEE floats. You can see more details if you convert the result to a fraction, recurring decimal or use the @debug syntax:

> e^27 to fraction
approx. 71410306039970762789283426758302889490289073593035075381168284413165149457863448019443560038308652663029372407173133034187188198203530227022531144468164243048436419743231672197262698596401880358756231663807677873554780496591618620802013102132422771631202132895592568968622654004708277232900763313847146852329953703338684960782694101353219947446863967226592329017448548867417089293353625274093016683159372434331196180565832672571758355860417492167761609813402330327318284776798663/134217728000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
> e^27.5 to fraction
approx. 113071459683578120472327099102336051648588891081218191369003649549756465261102237007694301508372881808662903082913176552102059568082115284428876870020757975453400867141105099565295953712688575209108205397183707258374426781403808874708210432861342001914095701604030903209919368490400387089900836561769421738856582698458968020002830418041272551242205248340822299743175604579244285544934426857254150309544686793419110800332082802079197543970598348380920361750514942527896031224345516790791027454605093/128900542851112339337197566172636293436602828097556818643071353806407934059695734309972907673747870834094000335784678507293322920070947831195209259568499511900956414314278326484884929480771416380632206420244014065736775812876898350132867352375991098916942676657973164846916206028216383319550901096847223141834174168450619069759725048719635153047876725099735783918863724930189777266272673902695537815472186553026226219478545119405743007493993119787437285413407591711559323225333690952373
> e^27.2 to fraction
approx. 540450167045008595792312004199834380334566558039217527674867169262566023029354549269943291534961887369582253370460426881638164349592917950901274799523416458906691472153703346060457781788609633533139821812387071456979050923486512969399038206775262133433143361773945970688525768622521222728493571352973442796328965489495449459161143584925706880095313563041218468946730192905652506725148068702017021518610385894221154268975585519199351505986414513367725746690430552626085552932492426803039465939/831659873107350606924277819049736548700633569116847123534643389560993233870343316290740405696569335829481700412202030651741112523405216520520258103826026453388012603343813419649546013870839562212187369038410042140117845996513183282593695923409242650749857012844173483349342134770810346513633839847772060655240617712159520702992514401353445472955930171488373469508630213195254916073981130655838797234155989329158534728973598752721979215918401941864343725316964178712306695143072393
> @debug e^27.2
approx. [1, 7656378254019563035, 4822374401389802161, 2864127233296097178, 4741800852526752869, 18012754266386254273, 11136535052342358123, 18411656901359152354, 18173108453789417059, 17246393702594385684, 3885025701834157573, 8808046853525194504, 15725393204157425962, 3666230793870136370, 10071584790018640278, 11747667919465341407, 15447276661555292251, 9714398317530110076, 16360410984259590712, 15423306616511918406, 15258806223664032888, 2910241913288895562, 11797270048586013691, 5586270268566419210, 12759910614835022521, 6885804640814783249, 6597273079670374400]/[40168216, 12140300740075376544, 16821918182857862720, 4274579849032474698, 10718370062722359081, 16624268609432783861, 15081792654813444287, 4733863717375642309, 1401035426730474920, 5531460526609523491, 15800261438749672432, 14580120276478650636, 2481329839418129549, 8256121093525486289, 15600448981039766854, 3476589167266583129, 6260927822833909417, 18074389192336914986, 9829285395187397321, 14304581701864546035, 2365637261319372188, 2234058393639125993, 13854629429954846079, 2148411450467894906, 13395467465607522549, 4941945520756097024] (unitless) (base 10, auto, simplifiable)

As a workaround, you can define your own less precise version of e, for example:

> e = approx. 2.718
> e^27.2
approx. 540871857735.3714207222

Unfortunately if your exponent has a lot of decimal places it will still be extremely slow. Any PRs to improve performance would be more than welcome!

I've also been considering adding a way to opt out of arbitrary-precision maths but this has also not been implemented.

I think a way to specify precision would be very helpful. I will see if I can try to figure out a way to make that work, keep in mind I have never contributed to an open source project before.

I've now merged in a performance improvement for exponentiation by decimal numbers, so calculations like e^27.2 should be quite a bit faster.

e^27.2 at least now shows 5 more decimals then is accurate also e^0.2*e^27 shows 9 more then is accurate, don't know if you know that

2^e takes alot of time also, it seems like you turn the number into a fraction but i can't find your fractions code to tell if it is fast or not, also 2^2.536543645276 should probably be computed as (2^(1/250000000000))^634135911319 instead of the big number first then the root. can't tell if that really matters for sure though. but probably is just best to use a taylors series to calculate this, i don't really see the benefit of what your doing

Fractions are too slow for this kind of computations in general, so for this case arbitrary precision floating point numbers are used if precision is an issue, larger float type such as float-256 or regular old floats