$gap = 0.2
$opDia = 0.4
$opGap = $gap + $opDia
$bigDia = 3 * $opDia
$bigGap = 2 * $gap + $bigDia/2
$angle = -90 # starting angle = north, 0°
$curl = 42 # degrees of curl per step
$relax = 0.90 # per-step decay factor toward minimum turn
$minCurl = 15 # lower floor prevents tight late loops
$constTC = 0x3F4954 # constant text/border color
$constBC = 0xE8EDF2 # constant background color
$hypTC = 0x3F4F8A # hyperbolic function text/border color
$hypBC = 0xE1E6F9 # hyperbolic function background color
$trigTC = 0x1F4E79 # trig function text/border color
$trigBC = 0xD9E9F7 # trig function background color
$ufnTC = 0x7A4A20 # unary function text/border color
$ufnBC = 0xF7E3CF # unary function background color
$implAC = 0xA8B9CB # implemented-in-terms of arrow color
define step { \
$angle -= $curl ; \
$curl = $minCurl + ($curl - $minCurl) * $relax ; \
arrow chop from 2nd previous circle to previous circle ; \
}
ELEMENTS: [
circle "eml" diameter $opDia fill white
move $gap/2
circle "1" same
]
EML: circle diameter $bigDia with .c at ELEMENTS.c \
color 0x8A4E1E fill 0xF1B36E \
behind ELEMENTS
E: circle "e" diameter $opDia \
with .se at $gap heading 0 from EML.nw \
color $constTC fill $constBC
step
EXP: circle "exp" same with .c at $opGap \
heading $angle from previous circle.c \
color $ufnTC fill $ufnBC
step
LN: circle "ln" same with .c at $opGap \
heading $angle from previous circle.c
step
ZERO: circle "0" same with .c at $opGap \
heading $angle from previous circle.c \
color $constTC fill $constBC
step
ARG: circle "arg(𝑥)" same with .c at $opGap \
heading $angle from previous circle.c \
color $ufnTC fill $ufnBC
step
SUB: circle "–" same with .c at $opGap \
heading $angle from previous circle.c
step
NEGX: circle "-𝑥" same with .c at $opGap \
heading $angle from previous circle.c
step
NEG1: circle "-1" same with .c at $opGap \
heading $angle from previous circle.c \
color $constTC fill $constBC
step
TWO: circle "2" same with .c at $opGap \
heading $angle from previous circle.c
step
ADD: circle "+" same with .c at $opGap \
heading $angle from previous circle.c \
color $ufnTC fill $ufnBC
step
INV: circle "1/𝑥" same with .c at $opGap \
heading $angle from previous circle.c
step
MULT: circle "×" same with .c at $opGap \
heading $angle from previous circle.c
step
POW: circle "𝑥ʸ" same with .c at $opGap \
heading $angle from previous circle.c
step
SQUARE: circle "𝑥²" same with .c at $opGap \
heading $angle from previous circle.c
step
DIV: circle "÷" same with .c at $opGap \
heading $angle from previous circle.c
step
HALF: circle "𝑥/2" same with .c at $opGap \
heading $angle from previous circle.c
step
AVG: circle "(𝑥+𝑦)/2" same with .c at $opGap \
heading $angle from previous circle.c
step
SQRT: circle "√𝑥" same with .c at $opGap \
heading $angle from previous circle.c
step
I: circle "𝒊" same with .c at $opGap \
heading $angle from previous circle.c \
color $constTC fill $constBC
step
LOG: circle "logₓ(𝑦)" same with .c at $opGap \
heading $angle from previous circle.c \
color $ufnTC fill $ufnBC
step
PI: circle "𝜋" same with .c at $opGap \
heading $angle from previous circle.c \
color $constTC fill $constBC
step
HYPOT: circle "√𝑥²+𝑦²" same with .c at $opGap \
heading $angle from previous circle.c \
color $ufnTC fill $ufnBC
step
EXPIT: circle "expit" same with .c at $opGap \
heading $angle from previous circle.c
step
COSH: circle "cosh" same with .c at $opGap \
heading $angle from previous circle.c \
color $hypTC fill $hypBC
step
SINH: circle "sinh" same with .c at $opGap \
heading $angle from previous circle.c
step
TANH: circle "tanh" same with .c at $opGap \
heading $angle from previous circle.c
step
COS: circle "cos" same with .c at $opGap \
heading $angle from previous circle.c \
color $trigTC fill $trigBC
step
SIN: circle "sin" same with .c at $opGap \
heading $angle from previous circle.c
step
TAN: circle "tan" same with .c at $opGap \
heading $angle from previous circle.c
step
ARCSINH: circle "sinh⁻¹" same with .c at $opGap \
heading $angle from previous circle.c \
color $hypTC fill $hypBC
step
ARCCOSH: circle "cosh⁻¹" same with .c at $opGap \
heading $angle from previous circle.c
step
ARCCOS: circle "cos⁻¹" same with .c at $opGap \
heading $angle from previous circle.c \
color $trigTC fill $trigBC
step
ARCTANH: circle "tanh⁻¹" same with .c at $opGap \
heading $angle from previous circle.c \
color $hypTC fill $hypBC
step
ARCSIN: circle "sin⁻¹" same with .c at $opGap \
heading $angle from previous circle.c \
color $trigTC fill $trigBC
step
ARCTAN: circle "tan⁻¹" same with .c at $opGap \
heading $angle from previous circle.c
step
arrow from LN to EXP thin color $implAC behind EML chop
arrow same from LN to EML
arrow same from ZERO to LN
arrow same from ARG to LN
arrow same from SUB to EML
arrow same from SUB to EXP
arrow same from SUB to LN
arrow same from NEGX to ZERO
arrow same from NEGX to SUB
arrow same from NEG1 to NEGX
arrow same from TWO to SUB
arrow same from TWO to NEG1
arrow same from ADD to SUB
arrow same from ADD to NEGX
arrow same from INV to NEGX
arrow same from INV to EXP
arrow same from INV to LN
arrow same from MULT to ADD
arrow same from MULT to EXP
arrow same from MULT to LN
arrow same from POW to MULT
arrow same from POW to EXP
arrow same from POW to LN
arrow same from SQUARE to MULT
arrow same from DIV to MULT
arrow same from DIV to INV
arrow same from HALF to TWO
arrow same from HALF to DIV
arrow same from AVG to ADD
arrow same from AVG to HALF
arrow same from SQRT to HALF
arrow same from SQRT to EXP
arrow same from SQRT to LN
arrow same from I to SQRT
arrow same from I to NEG1
arrow same from LOG to LN
arrow same from LOG to DIV
arrow same from PI to LN
arrow same from PI to NEG1
arrow same from PI to NEGX
arrow same from PI to SQUARE
arrow same from PI to SQRT
arrow same from HYPOT to ADD
arrow same from HYPOT to SQUARE
arrow same from HYPOT to SQRT
arrow same from EXPIT to EML
arrow same from EXPIT to EXP
arrow same from EXPIT to NEG1
arrow same from EXPIT to NEGX
arrow same from EXPIT to INV
arrow same from COSH to EXP
arrow same from COSH to NEGX
arrow same from COSH to AVG
arrow same from SINH to EML
arrow same from SINH to EXP
arrow same from SINH to COSH
arrow same from TANH to DIV
arrow same from TANH to COSH
arrow same from TANH to SINH
arrow same from COS to NEGX
arrow same from COS to SQUARE
arrow same from COS to SQRT
arrow same from COS to COSH
arrow same from SIN to SUB
arrow same from SIN to HALF
arrow same from SIN to PI
arrow same from SIN to COS
arrow same from TAN to DIV
arrow same from TAN to SIN
arrow same from TAN to COS
arrow same from ARCSINH to LN
arrow same from ARCSINH to NEG1
arrow same from ARCSINH to ADD
arrow same from ARCSINH to HYPOT
arrow same from ARCCOSH to NEG1
arrow same from ARCCOSH to SQRT
arrow same from ARCCOSH to HYPOT
arrow same from ARCCOSH to ARCSINH
arrow same from ARCCOS to ARCCOSH
arrow same from ARCCOS to COS
arrow same from ARCTANH to INV
arrow same from ARCTANH to TAN
arrow same from ARCTANH to ARCSINH
arrow same from ARCTANH to ARCCOS
arrow same from ARCSIN to SUB
arrow same from ARCSIN to HALF
arrow same from ARCSIN to PI
arrow same from ARCSIN to ARCCOS
arrow same from ARCTAN to TANH
arrow same from ARCTAN to ARCSINH
arrow same from ARCTAN to ARCSIN
→ /pikchrshowWhat Is Exp-Minus-Log?
EML is a mathematical construct unifying the most common operations on a scientific calculator in terms of the natural logarithm, created by Andrzej Odrzywołek and published to the arXiv pre-print service. I based this article on the “v2” publication, dated 2026-04-07.
Mathematically, this compound operator is written `eml(x,y) = e^x - ln(y)` The scheme requires nothing else other than the given constant “1.” Much like with a slide rule, the rest is defined in terms of these two foundational primitives.
The spiral diagram shows how everything relates,1 but do not get bogged down in the details at this point. One of the purposes of this article is to walk you through it step-by-step. For now, merely marvel at how everything stems from a few elements.
If you have a C47, an R47, or the simulator for either and wish to eat dessert first, you may browse the EML/Suite source code or download the R47 byte-code file and jump right in. There’s a fair bit to it, though, and so I encourage reading on.
But Is It Just Two Elements?
While one could in principle build a 36-function calculator based on this scheme having nothing more than two buttons labeled EML and 1, there’s more going on here than that.
The `e^x` operator itself (a.k.a. `exp(x)`) depends on having Euler’s number 𝒆 defined somewhere, plus a method of raising that to the 𝑥th power. Yet, this is quibbling, since a good many calculators do have an `e^x` button on them, including the R47 we use for our implementation of EML below.
The functions `e^x` and `ln(x)` are mutual inverses2 but because EML passes different arguments to each, subtracting their results doesn’t give 0 inside this operator. We get their general forms back out early in the EML scheme by passing in a few constants, as shown below.
Another thing we will handwave is the numeric input keys used to provide the 𝑥 and 𝑦 arguments to the EML function. Our hypothetical two-button calculator does not strictly require them, because all numbers can be synthesized from EML elements. At the same time, there’s a limit to how masochistic we wish to be in pursuing this project.
Synthesizing “Two”
And yet, we find it useful to begin with such a synthesis, to get ourselves started. The EML scheme synthesizes the constant “2” from:
- the “1” we claimed as a foundational element
- a previously synthesized “-1” value; and
- the arithmetic minus operator.
How? Easy: `2=1−(−1)`
There is a longer path that does not depend on previously having synthesized -1, being `2=1−((1−1)−1)` That turns out to be unnecessary, but to see why, we have to take our first deeper look at the spiral diagram above; just a peek for now…
$gap = 0.2
$opDia = 0.4
$opGap = $gap + $opDia
$bigDia = 3 * $opDia
$angle = -90 # starting angle = north, 0°
$curl = 42 # degrees of curl per step
$relax = 0.90 # per-step decay factor toward minimum turn
$minCurl = 15 # lower floor prevents tight late loops
$constTC = 0x3F4954 # constant text/border color
$constBC = 0xE8EDF2 # constant background color
$ufnTC = 0x7A4A20 # unary function text/border color
$ufnBC = 0xF7E3CF # unary function background color
$implAC = 0xA8B9CB # implemented-in-terms of arrow color
define step { \
$angle -= $curl ; \
$curl = $minCurl + ($curl - $minCurl) * $relax ; \
}
ELEMENTS: [
circle "eml" diameter $opDia fill white
move $gap/2
circle "1" same
]
EML: circle diameter $bigDia with .c at ELEMENTS.c \
color 0x8A4E1E fill 0xF1B36E \
behind ELEMENTS
EXP: circle "exp" diameter $opDia \
with .se at $gap heading 0 from EML.nw \
color $ufnTC fill $ufnBC
step
LN: circle "ln" same with .c at $opGap \
heading $angle from previous circle.c
step
ZERO: circle "0" same with .c at $opGap \
heading $angle from previous circle.c \
color $constTC fill $constBC
step
SUB: circle "–" same with .c at $opGap \
heading $angle from previous circle.c \
color $ufnTC fill $ufnBC
step
NEGX: circle "-𝑥" same with .c at $opGap \
heading $angle from previous circle.c
step
NEG1: circle "-1" same with .c at $opGap \
heading $angle from previous circle.c \
color $constTC fill $constBC
step
TWO: circle "2" same with .c at $opGap \
heading $angle from previous circle.c
step
arc → chop from EML.n to EXP.e
arrow chop from EXP to LN behind EML
arrow same from LN to ZERO
arrow same from ZERO to SUB
arrow same from SUB to NEGX
arrow same from NEGX to NEG1
arrow same from NEG1 to TWO
arrow from TWO to EML thin color $implAC chop behind EML
arc same <- from NEG1.e to TWO.s thin color $implAC behind EML
arrow same from TWO.w to SUB.e
→ /pikchrshowWe see here a fragment of the diagram at the top of this article with inessentials removed and the dependency arrows drawn more clearly, allowing us to realize that the EML scheme arranges its results according to two simple rules:
- Each step is defined in terms of nothing more than its preceding elements.
- The steps are ordered to keep dependencies close; ideally, adjacent.3
This fragment says the same thing the bullet list above does: the pale arrows from the “2” node show the dependencies on two prior results, plus the foundational “1” we get with this scheme by definition. The “2” node must follow the “-1” node to satisfy this dependency ordering, which in turn must follow the “-𝑥” node, defined in terms of its preceding nodes, zero, and subtraction. The chains become less straightforward the further you go down the spiral, but that’s why we picked this early segment as our starting example.
The arrow pointing at the big orange bubble implies that the definition of “2” in this scheme also depends on eml itself, and while that is indeed true, it’s an indirect relationship via the subtraction operation, which is directly defined in terms of eml. See Figure S1 in the paper’s Supplementary Information for more explicit details on which results depend on which inputs.
Speaking of colors, the scheme used for the small bubbles in the diagram is:
- pale blue: derived constants4
- light orange: arithmetic functions
- darker blue: trigonometric functions
- pale violet: hyperbolic functions
Show Me!
Odrzywołek’s EML compiler expands the expression “2” thusly:
`eml(eml(1,eml(eml(1,1),1)),eml(eml(eml(1,eml(eml(1,eml(1,eml(eml(1,1),1))),1)),eml(1,1)),1))`
Yow! If you crunch through all that, you will compute the constant two by using — as promised — nothing other than the foundational constant `1` and the `eml()` operator.
But you don’t have to take my word for it. I present here an R47 program to calculate it:
# Odrzywołek’s operator eml(x,y) = e^x - ln(y) takes its arguments
# from the stack, in that order: push _y_ value first, **then** _x_.
LBL ‘EML’
𝑒ˣ
𝑥⇄𝑦
ln(𝑥)
-
RTN
# RPN form of the EML compiler output for the expression “2”.
LBL ‘EMLTwo’
1
1
1
XEQ ‘EML’
1
1
1
1
XEQ ‘EML’
XEQ ‘EML’
1
XEQ ‘EML’
1
XEQ ‘EML’
XEQ ‘EML’
1
XEQ ‘EML’
XEQ ‘EML’
XEQ ‘EML’
1
1
1
XEQ ‘EML’
XEQ ‘EML’
1
XEQ ‘EML’
XEQ ‘EML’
RTN
END
The EMLTwo subroutine is that horrid expression above rewritten into RPN form, and the EML implementation is based on this one, created by HP Museum Forum member @dm319, but with one critical difference: it takes its arguments in the opposite order, allowing it to avoid one of the swaps.
Note that this will not run in the R47’s SSIZE4 mode, as may be apparent even without XEQ’ing it by observing that run of four “1” values. Without a fifth stack slot to hold the result of the prior EML call, we lose an intermediate calculation step and get the wrong answer.
From This, Infinity
The existence proof above puts us on a path to a simple but profound conclusion: we can get every other number by extending this same basic scheme.
- The addition operator produced a few steps down the spiral from the “2” node yields the natural numbers (ℕ) via the repeated successor function, continually adding 1.
- Negation gets us the other half of the number line.
- One of this article’s minor extensions fills that gap, putting the full set of integers (ℤ) at hand.
- Division lets us transform those integers into all rational numbers, ℚ.
- The power function `x^y` yields a finite-precision subset of the reals (ℝ) as integers multiplied by a power of ten, as calculators like the R47 do in fact store them internally. Technically, this is not closure over ℝ, but achieving that would require infinite memory and compute time under any practical implementation of EML.
- The irrational numbers are the subset ℝ∖ℚ, meaning all reals that are not among the rational numbers. EML encodes 𝒆 directly, and halfway down the spiral, it produces 𝛑.
- Three steps down from division, EML produces `sqrt(x)`, which combines with the derived constant “-1” and either addition or subtraction to produce the complex numbers (ℂ) from the reals.
By the time we get to this spiral’s end, we have all the tools we need to produce any result you could express on a scientific calculator having this same set of operations.
Is It Merely an Impressive Stunt?
It is understandable to think so, but keep this impractical scheme’s prime purpose in mind: to demonstrate that many calculator operations can be derived from two foundational elements.
A large part of the reason the above code is so ugly is that we skipped steps above in order to get to a salient example. Let’s back-fill the nodes in the spiral and see where that gets us:
# Odrzywołek‘s operator eml(x,y) = e^x - ln(y) takes its arguments
# from the stack, in that order: push _y_ value first, **then** _x_.
LBL ‘EML’
𝑒ˣ
𝑥⇄𝑦
ln(𝑥)
-
RTN
# RPN forms of the EML compiler output, reduced to minimal forms,
# and including only that subset of the chain leading to "2".
LBL ‘EMLExp’
# Unary: takes x on stack for 𝑒^x
1
𝑥⇄𝑦
XEQ ‘EML’
RTN
LBL ‘EMLLn’
# Unary: takes x on stack; computes ln(x) = eml(1,eml(eml(1,x),1))
1
XEQ ‘EML’
XEQ ‘EMLExp’
1
XEQ ‘EML’
RTN
LBL ‘EMLZero’
# Argless; yields the constant 0 = ln(1)
1
XEQ ‘EMLLn’
RTN
LBL ‘EMLSub’
# Binary: computes y-x = eml(ln(y),e^x)
XEQ ‘EMLExp’ ; y, e^x
𝑥⇄𝑦 ; e^x, y
XEQ ‘EMLLn’ ; e^x, ln(y)
XEQ ‘EML’ ; e^(ln(y)) - ln(e^x) = y - x
RTN
LBL ‘EMLNegX’
# Unary: computes -x
XEQ ‘EMLZero’
𝑥⇄𝑦 ; swap that 0 above the passed x as the y in…
XEQ ‘EMLSub’ ; y-x = 0-x = -x
RTN
LBL ‘EMLNeg1’
# Argless; negates EML's foundational "1" for later reuse
1
XEQ ‘EMLNegX’
RTN
LBL ‘EMLTwo’
# Argless; yields the constant 2 = 1-(-1)
1
XEQ ‘EMLNeg1’
XEQ ‘EMLSub’
RTN
END
Considerably less frightful! (Download the P47 version.)
The final version of this code ended up a smidge more complicated, but let us ignore those details for now and focus on some of the more noteworthy results of this refactoring:
- This version is shorter if you discount
LBL,RTN, comments, and blank lines: 22 actual program steps instead of 27, not counting the keyEMLroutine shared with the flattened version. I believe this is due to all the factored-out helper functions, which has the nice side benefit of bringing our stack usage within RPN’s traditional 4-slot limit. - The “cheating” use of a swap in
EMLExpis justified by the nature of the RPN stack, which requires a fixed parameter ordering. For our own sanity, we wish to maintain the convention that the stack’s X and Y registers are the same as the 𝑥 and 𝑦 variables in the EML expressions. One of our overarching goals is to reduce the amount of swapping to a minimum, but we see little point in striving to eliminate swaps entirely. We see the value of this stance shortly in that… EMLSubtakes its parameters in the same order as an RPN calculator’s “–” operator key by asking the EML compiler to expand 𝑦-𝑥 instead of 𝑥-𝑦.EMLTwopacks down nicely now that it has foundations to build on; it is the RPN form of `2=1−(−1)` seen above.
I used an LLM to perform most of the reductions you see here, but I did hand-check them afterward.
This version of the program drops EMLe not merely because we don’t need it to produce “2” in this example, but because nothing at all within the scope of EML as presently defined requires it, either. The paper’s SI indicates this in its Figure S1 by the light gray column above `e`. You can find it in the final suite for the same reason the paper shows how to produce it:
`eml(1,1)=e^1-ln(1)=e-0=e`
The simplified bubble diagram above lacks an `e` node for the same reason; ditto the suite’s `arg(x)` addition.
Completing the Spiral
The remaining functions along the Odrzywołek spiral turn out to be straightforward once simplified using these same methods. A few noteworthy observations can nevertheless be made.
Logistic Sigmoid Function
What the paper calls “σ” is not the standard deviation, as found on the R47’s 🟧 STAT menu, but instead the logistic sigmoid function. We use its short name “expit(𝑥)” to side-step the confusion here. Nothing in the EML scheme depends on having an implementation of this; it was apparently included purely because it falls out of the scheme for free.
Figure S1 in the paper’s SI specifies that it depends directly on the `eml(x,y)` operator itself, plus `-x`, `-1`, `e^x`, and `1/x`. My favorite clanker claims this is the shortest way to achieve that, within those constraints:
LBL ‘EMLSigP’
XEQ ‘EMLNegX’
XEQ ‘EMLNeg1’
XEQ ‘EMLExp’
𝑥⇄𝑦
XEQ ‘EML’
XEQ ‘EMLInv’
RTN
Before I shackled it thus, the same LLM took advantage of its temporary freedom and came up with something considerably simpler:
LBL ‘EMLSigA’
XEQ ‘EMLNegX’
XEQ ‘EMLExp’
XEQ ‘EMLNeg1’
XEQ ‘EMLSub’
XEQ ‘EMLInv’
RTN
That computes the function through subtraction of a negative number to produce addition: `1/(e−x - (-1))`
The one I like better than either is this one:
LBL ‘EMLSigR’
XEQ ‘EMLNegX’
XEQ ‘EMLExp’
1
XEQ ‘EMLPlus’
XEQ ‘EMLInv’
RTN
It implements the normal definition of this function `(1/(1 + e−x))` without violating any of the EML restrictions, with one less XEQ.
One wonders why neither of these simpler formulations was used in the paper.
Arbitrary Log
A similar case exists for the arbitrary logarithm. The paper says this should depend on `ln(x)` and division, which makes sense given the well-known logarithm identity:
`log_x(y) = ln(y) / ln(x)`
I once saw that masking-taped to the front of an HP-12C to address the fact that it only has natural logs by reminding its user that there’s an easy way to get all other log bases. This same fact results in this implementation:
LBL ‘EMLLogA’
𝑥⇄𝑦 ; calc dividend first; division is order-dependent
XEQ ‘EMLLn’
𝑥⇄𝑦 ; put it back where we got it, then
XEQ ‘EMLLn’ ; calc divisor, and
XEQ ‘EMLDiv’ ; divide
RTN
But double swapping bugs me! It suggests something wasn’t done in the right order to begin with, and that it’d be better to adjust the algorithm. Doing so gives us this EML-compatible variant having a single swap:
LBL ‘EMLLogR’
XEQ ‘EMLLn’ ; compute divisor first, but then
XEQ ‘EMLInv’ ; flip it, and
𝑥⇄𝑦 ; swap it with y so that when we…
XEQ ‘EMLLn’ ; …compute the dividend,
XEQ ‘EMLMult’ ; multiplying the inverse becomes division
RTN
If the automated solution-finding process the author developed considered this variation at all, it would have rejected it on the grounds of taking one more step than the one above, because way up in the thin air of abstract math land, details like stack register ordering doesn’t signify. Down here at ground level, what might concern us more is that this is likely to be more lossy, but this variant continues giving me the warm fuzzies despite its inherent flaws.
Famous Identities
Another thing worth noting in my R47 code is how many famous logarithmic identities it uses, a fact utterly obscured by the “flattened” form produced by Odrzywołek’s EML compiler, yet implicit in its output. In addition to the arbitrary log identity given above, the RPN code I present here uncovers the following:
`1/x=exp(-ln(x))`
`x·y=exp(ln(x)+ln(y))`
`sqrt(x)=exp(ln(x)/2)`
`y^x = exp(ln(y)·x)`
`ln(-1)=iπ; ∴ pi = sqrt(-((ln(-1))^2))`
This then blends over into the trigonometric sort:
`e^(ix)=cos(x)+i*sin(x)`
`cos(x) = exp(i·x); i = ln(-1)/pi`
`cos^-1(x) = cos^-1(cos(x))`
`cosh(x)=avg(exp(x),exp(-x))`
`sin(x) = cos(π/2 - x)`
`sin^-1(x) = pi/2 - cos^-1(x)`
`sinh(x) = eml(x,exp(cosh(x))) = e^x - cosh(x)`
`sinh^-1(x) = ln(x + hypot(x,-1))`
`tan(x) = sin(x)/cos(x) ; tanh(x) = sinh(x)/cosh(x)`
`tan^-1(x) = sin^-1(tanh(sinh^-1(x)))`
`tanh^-1(x) = sinh^-1(1/(tan(cos^-1(x))))
Given that we haven’t used all those listed in the linked Wikipedia articles, one anticipates further low-hanging fruit for the EML scheme to gobble up.
As it stands, these observations give us confidence by avoiding the need for independent mathematical proofs where EML expressions reduce to a proven form.
Subroutine Inventory
Our version of the spiral diagram shows 37 bubbles, counting the starting pair, but we find 45 subroutines defined in EML/Suite’s source code. The catalog of differences is:
- 4 variants of expit(x), counting
EMLSig* - 2 variants of
EMLLog* - 2 test routines:
EMLBall,EMLStkU - 1 HP-12C style
%function (EMLPct) to show one of the paper’s fans who doubted it could be done that it could so, hah! - 1 helper (
EMLTen) reused by bothEMLPctandEMLBall
That comes to 10 total, but two of those are part of the the spiral diagram’s set, which gives us `45-10+2=37`.
Our diagram in turn includes several additions relative to the one in the paper, reflected in the suite:
EMLZeroproduces “0” as natural fallout of the EML compiler’s `ln(x)` and `-x` implementations, saving repeated code. The paper does not include this natural result of EML in its diagram, though it is at least as significant as the other constants it does show as resulting from its scheme.I inserted it right after synthesizing the generalized `ln(x)` implementation from the EML definition’s internals because it is defined as `0=ln(1)`. I could have deferred its definition until after synthesizing subtraction by making fuller use of the foundational “1” element (`-1=1-1-1`) but that takes one extra step, and in the EML scheme, we use the mathematical definition of “complexity,” not the CS one.
EMLiprovides the imaginary number 𝓲, which has apparently gone imaginary in the paper’s diagram as well. 😜 I put it right after `sqrt(x)` in my version of the diagram, since that plus the previously derived -1 constant is all we need to produce it. My best guess as to the reason the paper doesn’t do the same is that the EML compiler emits multiple different schemes to produce this constant.EMLCoscomputes it not as `sqrt(-1)` but as `i=ln(-1)/π`, which actually gives `-i` on the R47, thankfully equivalent in this context. CallingEMLihere instead works standalone, but it breaks the round-tripping in theEMLBalltest, which suggests the reason for all this is to provide consistent branch handling in complex-valued functions.EMLArgcomputes the `arg(x)` function, given on the R47 as🟦∡, or🟦CPX🟧∡. This has inherent value, and it is perfectly within the spirit of EML, but the real reason I added it was to document a quirk of the wayEMLiworks.
Those three explain why my diagram has 37 bubbles and the one in the paper 34.
There is one further discrepancy in the paper’s “Calc-36” accounting worth mentioning: the author lists the 𝑥 and 𝑦 parameters to the various functions as primitives that went into the search for EML, but then makes the unwarranted leap that all 36 correspond to calculator buttons. In RPN terms, these correspond to stack positions. None but a calculator manufacturer’s marketing department would count these as “functions;” they are variable parameters to functions.
Building an R47 Menu
Tying all these functions into a custom menu gives us a better interface for calling these routines than repeated XEQ keypresses. I suggest taking over the R47’s MyMenu for this purpose, though you could instead take over HOME. How you arrange the functions and which subset of 18 you choose is up to you. I show one idea here, focusing on EML’s arithmetic functions.
I think the result flows nicely, with the basic four operators bottom-center, misc functions on the left, powers on the right, and functions related to each operator in columns above them. I’ve provided an R47 state file for download, including this menu and the programs backing it. Call SAVEST first: although I have pared this file down to the essentials, it still might overwrite something you consider precious when the R47 merges the two.
Transcendental Functions Menu
But let’s do something a bit different, compatible with assigning an R47 “ribbon” overlay. My favorite is: 🟦 KEYS RIBBONS 🟧 M.SAV+. Go ahead and apply that now.
Next, download the P47 version of our EML suite, upload it to your R47, and READP it in using the fine new ribbon you’ve just installed.
Long-press the 🟦 button to bring MyMenu up, then:
🟦 ASN 🟧 CAT PROGS EMLSin EXIT 🟧 F1
🟦 ASN 🟧 CAT PROGS EMLCos EXIT 🟧 F2
🟦 ASN 🟧 CAT PROGS EMLTan EXIT 🟧 F3
🟦 ASN 🟧 CAT PROGS EMLATan EXIT 🟧 F4
🟦 ASN 🟧 CAT PROGS EMLACos EXIT 🟧 F5
🟦 ASN 🟧 CAT PROGS EMLASin EXIT 🟧 F6
🟦 ASN 🟧 CAT PROGS EMLSinH EXIT 🟦 F1
🟦 ASN 🟧 CAT PROGS EMLCosH EXIT 🟦 F2
🟦 ASN 🟧 CAT PROGS EMLTanH EXIT 🟦 F3
🟦 ASN 🟧 CAT PROGS EMLATnH EXIT 🟦 F4
🟦 ASN 🟧 CAT PROGS EMLACsH EXIT 🟦 F5
🟦 ASN 🟧 CAT PROGS EMLASnH EXIT 🟦 F6
Or, download and then load it, with the above caveats in mind.
You should end up with something that looks like this screenshot:
I would not blame you if you did not like the long menu item names. I did that because I wanted everything grouped together in my program catalog and wanted to reduce the risk of colliding with other people’s programs to an acceptably low level. You could choose to rename the subroutines to something more idealized.
While the suite’s trigonometric and hyperbolic functions are hard-coded for radians, it isn’t a problem in practice because the R47 is strongly-typed, allowing any of these functions that take an angle to accept values tagged with any of the R47’s several angle data types. Functions that return an angle will always give it tagged with the radian angle type, which then means getting it into any other form is as easy as hitting the R47’s DRG button until it transforms the result into the form you prefer. You may need to chop off any imaginary error residue left behind first, however. The code cannot safely do that for you because it affects how chained calculations are handled, as in EMLBall.
All this means the source code is not portable to other machines via rejig as I had hoped, nor is it pure EML, but I felt I had to do this to avoid complaints that EML/Suite is “buggy” when it is performing as designed, merely not as the user expects.
Logarithms & Exponentials Menu
Here’s another idea along the same lines, collecting EML’s logarithmic/exponential functions. It is paired with the M.CPX ribbon and loosely follows its column ordering. (Download)
Because there would be gaps otherwise, I’ve filled the R47’s native versions in for comparison purposes. The suite does not include a base-10 log function, but you can combine 🟦 EMLTen and 🟦 EMLLogA to synthesize it, then compare its results to the native LOG version situated below them. Another space-filling exercise is exposing all four versions of the expit(𝑥) function: our R47 native implementation and the three EMLSig* variants.
Impracticalities
I would never suggest that anyone transition their daily calculations over to any form of EML, much less this amateur implementation. There are a number of problems with it, which are all solved one way or another by using your calculator as designed:
It’s inefficient
Even a maximally efficient hardware implementation would likely be slower than any comparable traditional implementation.
It’s lossy
Implementations using dedicated logic instead of reusing a few core primitives many times over will have an advantage in avoiding precision loss.
As but one example, take our5 division implementation, implemented by inverting X and then multiplying that with Y. That’s perfectly kosher in an ideal mathematical world, but fixed-precision calculation engines like the one in the R47 are decidedly impure.
The order of trig operations in the menu example above is no accident; it is the one needed for my Ballpark Accuracy article. I bring that up here because the result is illuminating: EML on the R47 puts it into the `10^-32` class, an order of magnitude worse than its native performance on the test’s radian flavor. One could choose to take this as justification for adding RSD 31 calls to the end of most subroutines, but I merely speculate, having not tried it on the grounds that I prefer to see the ugly truth over a best-possible approximation in cases like this.
It’s complex
Literally! Because every operation goes through logarithms, the unwary will find complex numbers popping out unexpectedly.
This point combines with the previous one to explain why these routines will claim `42÷2 = 21.+i·1.6856…*10^-33` on the R47: the imaginary part is a residual error term. While it is very near zero, any decent calculator with a complex number feature — wait, isn’t that redundant? — will give flat zero for the imaginary part here. I’ve scattered calls to the R47’s Re function about the suite to combat this where justified, but I can’t do that for division; you might give a complex number as an argument! Instead, I chose a passive stance of allowing the ugly truth to shine forth, for educational value if nothing else.
It’s noisy
EML/Suite uses the stack to produce intermediary results, whereas the calculator builtins keep all that hidden away, pushing only the final output to the stack. While my implementation could include a bunch more stack manipulation to solve that, it would be at a cost to clarity. I expect an experienced RPNer to be able to understand EML purely from reading the source code, which incentivizes me to elide all unnecessary overhead.
It isn’t all bad news. EML/Suite is stack-balanced, meaning none of these routines leave “litter” behind. Unary routines replace their argument with the result, and the argless ones push their lone result onto the stack as the new X; the internal calculations do consume stack slots, but they leave as much of the pre-call stack behind as possible. The only cases where an EML/Suite routine appears to litter the stack with intermediary results are those that fill the stack, triggering RPN’s duplicate-on-drop rule. This only occurs for those whose header comments say “takes 4 slots” when run in SSIZE4 mode. None of the routines take all 8 slots in the R47’s default SSIZE8 mode.
The only EML routines that consume no stack spots for intermediate results are EML itself and our utterly trivial EMLOne routine, both being implemented entirely with builtins. Even the native expit implementation provided as a testing baseline must take one slot for the addition step.
These facts make it difficult to compose these routines with other RPN code.
Lowering
Once I stabilized EML/Suite, the next thing I wanted to find out was how far down the RPN calculator power chain I could lower it:
- HP-42S family — related to R47, but considerably less advanced, and it shows
- HP 35s — enough complex number support to rival the 42S
- HP-32S family — also has complex number support, but as it turns out, not enough
- HP-15C family — roughly as capable as the 35s on this test, if more primitively presented
- HP-12C family — near rock bottom, but some elements of EML remain to be seen
Conclusion
I am not qualified to judge whether all this EML stuff will end up being judged by history as mere recreational mathematics, as a candidate for a Fields medal, or somewhere in between. I created this suite purely for its entertainment and educational value. I wish you some of each from it.
If nothing else, you should now see what makes the natural log natural. If not, go back to the top and pay attention this time.
(You may now wish to return to my R47 article index.)
License
This work is © 2026 by Warren Young and is licensed under CC BY-NC-SA 4.0
- ^
It is based on one on the penultimate page of the paper, but redrawn both for my own purposes and to collect the foundational elements in the center. The original version implies that the “1” node is derived from
eml, but that is not the case. There are other differences as well: reflect our additions, move `pow(x)` closer to `×`… - ^ Starting from the identity `log_b(x) = a iff b^a=x` we can reduce that to `ln(x) = a iff e^a=x` for base `e`. The full picture gets complicated.
- ^ In practice, only about half the results depend on the one immediately prior to it on the spiral. For instance, the diagram implies that subtraction depends on having “zero” defined first, where it actually jumps past it to `ln(x)`, `exp(x)`, and `eml(x,y)`.
- ^ This as opposed to the given constant “1.”
- ^ I say “our” because my code follows the paper’s guidance on which functions should be implemented in terms of which others. That in turn is constrained by the author’s goal of bootstrapping the EML system up from primitives, of which there are only about a dozen at the point it synthesizes the division operator. A designer given free rein wouldn’t implement division like this at all.