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