Re: Inconsistencies and special treatment of %e vs. %pi (and numer, %enumer mess)
Richard Fateman <[email protected]> Tue, 9 Jun 2026 14:10:26 -0700
| Newsgroups | gmane.comp.mathematics.maxima.general |
|---|---|
| Message-ID | <CADB8Zm5HkCSFBcz4xXj=YfRDP27wrRehx-7+gdH=a1xZQB7mUA@mail.gmail.com> |
Not that it is still the same as when I wrote bfloat() many years ago, but the idea was, functionally, to try to make everything possible into a big float. I don't know if ev(..., bfloat) was part of the design. Maybe thinking of the semantics functionally would help. That is, float(..) is a function. numer(...) is a function. Possibly flags like float( ...., keep_%e=true) ?? Maybe make Stavros happy to not use ev(...)? Just a thought. RJF On Tue, Jun 9, 2026 at 1:30 PM Stavros Macrakis <[email protected]> wrote: > Re *%pi^x*, I don't think that is ever really used in math, and so no one > cares. > > Understood about idea vs. implementation, but I don't much like the idea > of having mathematical transformations performed in evaluation. I've been > trying for years to wean people off of the overuse of *ev*, and this > gives a new reason to use *ev*. As far as I'm concerned, it should be > possible to perform *all* mathematical operations without involving > evaluation at all. We're not there yet -- for example, *nouns* today is > bound up with evaluation (*ev(...,nouns,noeval)* doesn't replace noun > forms with verb forms) -- but I'd like to move in that direction. > > On Tue, Jun 9, 2026 at 3:53 PM David Scherfgen <[email protected]> > wrote: > >> Interesting observation regarding *bfloat* vs. *float*. >> >> As I said, I agree with the idea of leaving *%e^x* alone even if >> *numer=true* unless *x* is numeric. >> >> (By the way, wouldn't that also apply to *%pi^x*? Is it ever desirable >> to rewrite that as *3.141^x* unless *x* is purely numeric?) >> >> My main point is that while the *idea* is good, the current >> implementation is incorrect (see examples I posted) and relies on lots of >> intertwined special-case code. What I find the most "disturbing" is that >> the *simplifier* turns *%e* into *2.718*, making it the only atom that >> the simplifier can modify. This is bad. One would think that an atom is >> inherently simplified, and it cannot have a *simp* tag. >> >> What if we rip out all the special treatment of *%e* in the *simplifier* >> and leave it to the *evaluator* to replace it with its numeric value? >> Then we can try to make *%enumer* work in the evaluator, i.e. prevent >> *%e^x* with symbolic *x* from becoming *2.718^x*. >> >> >> Stavros Macrakis <[email protected]> schrieb am Di., 9. Juni 2026, >> 20:57: >> >>> My guess is that the purpose of special-casing *%e^x* is that people >>> think of it as a notation for the function *exp(x)*. It is ever >>> desirable to rewrite *%e^x* as *2.718^x *unless *x* is purely numeric? >>> There is nothing analogous for *%pi *or other constants. >>> >>> By the way, I see that *bfloat(...)* preserves symbolic *%e *when it is >>> the base of an exponentiation, because it does not work via simplification. >>> *float(...)*, on the other hand, does not preserve it. >>> >>> I'll also note that *xxx,float == ex(xxx,float)* is equivalent to >>> binding *float:true*, whereas *xxx,bfloat == ex(xxx,bfloat)* is >>> equivalent to *bfloat(xxx)*... which in most cases results in similar >>> results. But I wouldn't dare to make them consistent because I don't know >>> how much user code it would break. >>> >>> On Tue, Jun 9, 2026 at 1:10 PM David Scherfgen via Maxima-discuss < >>> [email protected]> wrote: >>> >>>> Dear all, >>>> >>>> First, please excuse the rather long mail. If it's too long for you to >>>> read it completely, please read only the example codes that show the >>>> problem, and then skip to "TLDR / Way forward?". >>>> >>>> While digging into the simplifier and evaluator, I noticed that %e >>>> receives an almost ridiculous amount of special treatment compared to, >>>> e.g., %pi. >>>> >>>> - %e is the *only atom* that can be simplified to something >>>> different by the simplifier: If the global flags numer and %enumer >>>> are both true, %e is simplified to its numerical value. It's likely >>>> that there is code at other places that (wrongly, but understandably) >>>> assumes that atoms are inherently simplified and don't need to go through >>>> the simplifier, which would cause bugs. The other constants like %pi >>>> aren't handled by the simplifier. >>>> - There is some really ugly hackery going on in the evaluator that >>>> explicitly targets %e in conjunction with numer/%enumer. >>>> >>>> The global flag %enumer = false (default) is supposed to "protect" %e >>>> from becoming a float when numer is true in cases of %e^x, where x is >>>> symbolic. At first glance, this works: >>>> >>>> (%i1) %e^x + sin(%e), numer; >>>> (%o1) %e^x + 0.41078129050290885 >>>> >>>> Here, %e^x is correctly left alone, while sin(%e) becomes a float. >>>> The following also works: >>>> >>>> (%i2) sin(%e^x), numer; >>>> (%o2) sin(%e^x) >>>> >>>> But now let's resimplify that with numer on (classically, this would >>>> be done with expand(%, 0, 0), but we have resimplify now): >>>> >>>> (%i3) resimplify(%), numer; >>>> (%o3) sin(2.718281828459045^x) >>>> >>>> Oops! Now %e became a float, even though it's the base with a symbolic >>>> exponent, which is exactly the case where it should have been "protected" >>>> by %enumer = false. >>>> >>>> What's going on here? >>>> >>>> - When typing just sin(%e^x), numer in the REPL, the evaluator >>>> starts by simplifying the leaves %e and x, then builds and >>>> simplifies %e^x, and finally builds and simplifies sin(%e^x). It >>>> works its way from the leaves to the root, *bottom-up*. >>>> - But when we resimplify, all simp tags are stripped from the >>>> entire expression, and the simplifier is called on the root, working in a >>>> *top-down* fashion. The sin simplifier calls simpcheck on its >>>> argument, which, due to a momentous commit described here >>>> <https://sourceforge.net/p/maxima/bugs/123/>, dynamically binds >>>> %enumer to numer (true). This "infects" the entire sub-tree, and >>>> all instances of %e inside it are converted to a float by the >>>> simplifier, no matter what. >>>> >>>> So we get a different outcome depending on whether we work bottom-up or >>>> top-down. It doesn't end there, though. Here's an example that shows how >>>> %e and %pi are treated in a fundamentally different way: >>>> >>>> /* OK, both become floats: */ >>>> (%i4) %pi*y + %e*x, numer; >>>> (%o4) 3.141592653589793*y + 2.718281828459045*x >>>> >>>> /* Not OK, %e stays symbolic: */ >>>> (%i5) [%pi, %e], numer; >>>> (%o5) [3.141592653589793, %e] >>>> >>>> /* If we write -%e instead of %e, it becomes a float: */ >>>> (%i6) [%pi, -%e], numer; >>>> (%o6) [3.141592653589793, -2.718281828459045] >>>> >>>> *TLDR / Way forward?* >>>> >>>> - In summary, the handling of %e, numer and %enumer is an >>>> inconsistent mess that needs lots of special cases in the code, and it >>>> doesn't even work as intended. >>>> - I think that, as much as possible, this extra treatment of %e >>>> should go away. It would make the code simpler and the results more >>>> consistent. >>>> - The idea behind %enumer = false isn't bad, but there's a >>>> fundamental problem with the way it's supposed to work: When working >>>> bottom-up, we first see %e, but *we don't have the context*, i.e. >>>> we don't know where that %e sits inside a parent expression (if >>>> any). >>>> - Is it the %e in %e^x? Then it must not become a float. >>>> - Is it any %e in %e^%e? Then it must become a float. >>>> - Is it the %e in sin(%e)? Then it must become a float. >>>> - Is it just a standalone %e? Then it must become a float. >>>> - There's one way it could work, although it comes with a catch: >>>> - We stop handling %e in simplifya entirely. *Nice side effect: >>>> Atoms are always simplified!* >>>> - Instead, we treat %e like any other constant, e.g. %pi, and >>>> let the evaluator convert it to float unconditionally when numer >>>> is true. >>>> - Afterwards, if %enumer is false, we look explicitly for >>>> 2.718281828459045^(symbolic) and convert that back to >>>> %e^(symbolic). Note that this is an exact comparison to %e-val >>>> that will not be affected by floating point issues. >>>> - The catch: If the user manually enters 2.718281828459045^x, it >>>> will become %e^x. Maybe that's acceptable? I don't see any other >>>> way of achieving the desired behavior. >>>> - Alternatively, we could degrade %enumer to a selective switch >>>> just for %e. With %enumer = false, it would always prevent %e from >>>> becoming a float when numer is true. But that would include things >>>> like sin(%e), where the user would probably expect a float. This >>>> behavior would probably not be very useful, and I don't like the idea of >>>> changing the meaning of a flag in a significant way, as it would destroy >>>> backwards-compatibility. >>>> >>>> Looking forward to the community's opinions on this. >>>> >>>> Best regards >>>> David Scherfgen >>>> _______________________________________________ >>>> Maxima-discuss mailing list >>>> [email protected] >>>> https://lists.sourceforge.net/lists/listinfo/maxima-discuss >>>> >>> _______________________________________________ > Maxima-discuss mailing list > [email protected] > https://lists.sourceforge.net/lists/listinfo/maxima-discuss > _______________________________________________ Maxima-discuss mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/maxima-discuss