# Complex numbers in Erlang

**URL:** <https://erlangforums.com/t/complex-numbers-in-erlang/1624>\
**Category:** Chat / Discussions\
**Tags:** numbers\
**Created:** [July 5, 2022, 8:47pm UTC](https://erlangforums.com/t/complex-numbers-in-erlang/1624 "2022-07-05T20:47:04Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![peerst](https://erlangforums.com/user_avatar/erlangforums.com/peerst/32/557_2.png) [@peerst](https://erlangforums.com/u/peerst)\
**Post date:** [July 5, 2022, 8:47pm UTC](https://erlangforums.com/t/complex-numbers-in-erlang/1624/1 "2022-07-05T20:47:04Z")

</div>

If we extend the numbers in Erlang I’d rather care more for complex numbers than rationals. Fixed point has some use in financial applications but they often go with integers + a fixed scale i.e. everything is in pico $

---

<div class="post-metadata">

**Author:** ![nzok](https://erlangforums.com/letter_avatar_proxy/v4/letter/n/4af34b/32.png) [@nzok](https://erlangforums.com/u/nzok)\
**Post date:** [July 5, 2022, 11:25pm UTC](https://erlangforums.com/t/complex-numbers-in-erlang/1624/2 "2022-07-05T23:25:07Z")

</div>

I have a module that does rational arithmetic in Erlang,  
but it doesn’t have proper documentation, types, or tests  
yet, though it has been informally tested. It’s about 170  
SLOC.

I don’t have anything similar for complex arithmetic,  
although I do have an implementation of Complex for  
Smalltalk. There are more than 130 functions defined  
on complex numbers and all up about 700 SLOC. The basic  
reason is that there are lots of functions we do expect  
to work on complex numbers that we do not expect to work  
on rationals, like trig and hyperbolic functions. But  
also, getting complex multiplication right is harder than  
you might think. The naive mathematical definition has  
numerical issues. And doing complex _division_ well is  
a nightmare, which has attracted decades of work. See  
[http://www.cs.otago.ac.nz/research/publications/OUCS-2017-07.pdf](http://www.cs.otago.ac.nz/research/publications/OUCS-2017-07.pdf)  
for an introduction to some of the issues. glibc, for example,  
does not do as well as I’d expected.

All things considered, the surprising difficulty of doing  
complex arithmetic is an excellent reason for it being  
provided in some fashion. Doing it efficiently requires  
embedding at a low level in the language, but if you need  
large amounts of complex arithmetic you should probably  
delegate that work to C or Fortran or OpenACC or OpenCL…  
Erlang being Erlang, a module won’t give you operator  
overloading. But it _could_ offer a common way to do  
complex arithmetic and get decent answers.

In contrast to rational numbers, there is good reason  
to treat complex numbers with zero imaginary part  
differently from reals: sqrt(-1) is an error but  
sqrt(complex(-1,0)) is complex(0,1).

What functions, other than the basic arithmetic operations,  
should such a module provide?

---

<div class="post-metadata">

**Author:** ![peerst](https://erlangforums.com/user_avatar/erlangforums.com/peerst/32/557_2.png) [@peerst](https://erlangforums.com/u/peerst)\
**Post date:** [July 6, 2022, 7:48am UTC](https://erlangforums.com/t/complex-numbers-in-erlang/1624/3 "2022-07-06T07:48:29Z")

</div>

Well I would start with all functions in `math` but having said that, there are a few missing for real numbers in there too (`math` doesn’t even provide all functions provided by C)

Plus of course a few useful complex specific ones (planned to collect the features of complex number supporting language since a while but there is only so much time)

---

<div class="post-metadata">

**Author:** ![mat-hek](https://erlangforums.com/letter_avatar_proxy/v4/letter/m/7ab992/32.png) [@mat-hek](https://erlangforums.com/u/mat-hek)\
**Post date:** [July 6, 2022, 9:14am UTC](https://erlangforums.com/t/complex-numbers-in-erlang/1624/4 "2022-07-06T09:14:16Z")

</div>

Complex numbers solve totally different problems IMO, but of course, it would be great to have them too, for example for Elixir’s machine learning.

---

<div class="post-metadata">

**Author:** ![peerst](https://erlangforums.com/user_avatar/erlangforums.com/peerst/32/557_2.png) [@peerst](https://erlangforums.com/u/peerst)\
**Post date:** [July 6, 2022, 9:41am UTC](https://erlangforums.com/t/complex-numbers-in-erlang/1624/5 "2022-07-06T09:41:46Z")

</div>

Yeah this sub thread should be split out … now if I only would know how to do this

---

<div class="post-metadata">

**Author:** ![peerst](https://erlangforums.com/user_avatar/erlangforums.com/peerst/32/557_2.png) [@peerst](https://erlangforums.com/u/peerst)\
**Post date:** [July 6, 2022, 9:42am UTC](https://erlangforums.com/t/complex-numbers-in-erlang/1624/6 "2022-07-06T09:42:58Z")

</div>

@AstonJ how can one split topics?

---

<div class="post-metadata">

**Author:** ![AstonJ](https://erlangforums.com/user_avatar/erlangforums.com/astonj/32/9_2.png) [@AstonJ](https://erlangforums.com/u/AstonJ)\
**Post date:** [July 6, 2022, 1:23pm UTC](https://erlangforums.com/t/complex-numbers-in-erlang/1624/7 "2022-07-06T13:23:26Z")

</div>

Done - if you would prefer a different title or section just let us know :023:

_Also in case you need it in future, if you click on the time stamp of any post and then on ‘New Topic’ you can create a linked thread 😃_

---

<div class="post-metadata">

**Author:** ![peerst](https://erlangforums.com/user_avatar/erlangforums.com/peerst/32/557_2.png) [@peerst](https://erlangforums.com/u/peerst)\
**Post date:** [July 7, 2022, 9:21am UTC](https://erlangforums.com/t/complex-numbers-in-erlang/1624/8 "2022-07-07T09:21:15Z")

</div>

> [@AstonJ](#):
>
> Also in case you need it in future, if you click on the time stamp of any post and then on ‘New Topic’ you can create a linked thread 😃

Thanks!

---

<div class="post-metadata">

**Author:** ![peerst](https://erlangforums.com/user_avatar/erlangforums.com/peerst/32/557_2.png) [@peerst](https://erlangforums.com/u/peerst)\
**Post date:** [July 7, 2022, 9:23am UTC](https://erlangforums.com/t/complex-numbers-in-erlang/1624/9 "2022-07-07T09:23:24Z")

</div>

> [@nzok](#):
>
> What functions, other than the basic arithmetic operations,  
> should such a module provide?

@nzok do you know any example of a language with excellent complex number support?

---

<div class="post-metadata">

**Author:** ![nzok](https://erlangforums.com/letter_avatar_proxy/v4/letter/n/4af34b/32.png) [@nzok](https://erlangforums.com/u/nzok)\
**Post date:** [July 7, 2022, 12:06pm UTC](https://erlangforums.com/t/complex-numbers-in-erlang/1624/10 "2022-07-07T12:06:01Z")

</div>

There are _languages_ and there are _implementations_.  
Fortran has had standard support for complex arithmetic for 60 years.  
Modern Fortran is a structured modular programming language which  
has support for parallelism via OpenMP - and even “accelerators”  
like GPUs via OpenACC and cluster-scale via MPI, and more recently  
direct built-in language support for parallelism via the coarray  
feature. If you need to do a LOT of complex arithmetic, you would  
do well to give modern Fortran very serious consideration.

I mentioned glibc’s implementation of complex multiplication, but that’s  
not a language problem. It is a limitation-of-modern-hardware problem.  
You can have any one of

- fast
- as accurate as possible
- robust

but not really any two of them. Look at the definition.

(a,b) \* (c,d) = (a_c - b_d, a_d + b_c)  
= (DOT((/ a, -b /), (/ c, d /)),  
DOT((/ a, b /), (/ d, c /)))

Ideally, we’d like to compute dot([a,b],[c,d]) with

- one rounding (accurate)

- no overflow or underflow unless the result really won’t fit (robust).  
Many modern computers have a fused-multiply-add instruction  
that computes a+b\*c, or dot([1,a],[b,c]), with these guarantees.  
There was no such instruction on Pentium but AMD and Intel both support  
fma in single and double precision, in _some_ models but not others.  
Like my laptops, sigh.

The last time I looked, glibc used the mathematical formula naively,  
which is right for “fast”, has robustness problems, and is vulnerable  
to catastrophic cancellation. This is a library implementation issue,  
not a _language_ issue, and for many applications, it’s the right  
choice.

If there is some special function you need on the complex numbers,  
chances are there’s a free library offering it.

Ada 83 didn’t have complex numbers, but Ada 95 added (waves hands  
over details) a package for complex types and arithmetic operations  
on them and another package with elementary functions (sqrt, exp, log,  
\*\*, trig functions and inverses, hyperbolic trig functions and inverses).  
Ada introduced a separate Imaginary type:  
Real has an imaginary part that is EXACTLY zero semantically,  
not an IEEE +/- epsilon signed zero.  
Imaginary has a real part that is EXACTLY zero semantically,  
not an IEEE +/- epsilon signed zero.  
Ada 202x adds nothing and changes little.

C++ had complex arithmetic back when cfront was the only compiler for it.  
Eventually, this was copied into standard C, with some trouble taken to  
handle IEEE signed zeros. (Which are not something Fortran was designed  
to cope with, and it took some time to sort out the details.) C99 allows  
an imaginary type (for the same reasons as Ada) but does not require it.  
With the exception of cproj, it offers no function that Ada does not.  
I find it irritating that C has sqrt and cbrt and csqrt but no ccbrt.

On a typical Unix system, gfortan, gnat (Ada), and gcc will all use the  
same compiler back-end and much of the same libraries. Even if you use  
clang or the AMD or Intel compilers for C and/or Fortran, they’re still  
intended to interoperate, so when it comes to supporting complex numbers  
they’ll be much of a muchness.

PL/I had complex back in the 1960s, but who cares about PL/I these days?

Common Lisp has pretty good complex arithmetic support, at least as far  
as the language is concerned. It always felt more like a native part  
of the language. (In PL/I, _nothing_ felt like a native part of the  
language, and yes, I have used it.) Scheme language support is OK, but  
complex numbers are optional in Scheme, and implementors tend to have  
other priorities.

It may or may not be of interest that the core work on defining branch  
cuts for programming language implementations of complex elementary  
functions was done for APL. The APL system I use has truly excellent  
support for all sorts of numeric types. I just have to run it under a  
Windows emulator, and oh well, the manual could use a lot of work.

C# and F# share System.Numeric.Complex, which is pretty yawn-worthy.  
Complex being a struct rather than a class, they don’t have to allocate  
complex numbers on the heap. They certainly don’t add any interesting  
built-in functions, such as the Hankel functions I once had a need for.

I like Smalltalk, and several Smalltalk systems support Complex. But)  
some of them are frankly insane (like trying to treat complex numbers  
as totally ordered, or worse, defining a total order that isn’t one).  
That’s why I had to write my own. (And, here’s a tip: I repeatedly used  
Common Lisp for guidance.) I’m happy that my Smalltalk implementation of  
Complex does a decent job. For example, Complex atRandom: rng yields a  
number randomly and uniformly selected from the unit disc. BUT complex  
numbers are always allocated on the heap, so forget about performance.

We can consider three criteria:

- speed (Erlang will not fare well here)
- accuracy (fast and accurate are not always compatible)
- coverage (how many functions are provided).

All of Fortran, C++, C, C#, F#, and Ada offer much the same set of special  
functions for complex numbers. Only Ada offers Cot and Coth; only C offers  
cproj, but that’s about it.

And THAT is why I asked what functions are actually needed.  
How about (log) gamma and (log) beta over complex numbers?  
Cylindrical functions? Henry Baker had a nice paper showing  
how to define floor, ceiling, round, truncate. All of those?  
Complex analogues of C’s modf and ldexp?

\
