printfn / fend

Arbitrary-precision unit-aware calculator
https://printfn.github.io/fend
MIT License
587 stars 50 forks source link

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

Open Nicbudd opened 3 months ago

Nicbudd commented 3 months ago

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.

printfn commented 3 months ago

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.

Nicbudd commented 3 months ago

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.

printfn commented 2 months ago

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.

bgkillas commented 2 months ago

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

bgkillas commented 2 months ago

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