I was recently reminded of the ability of Emacs Calc to perform computations on values with units. Like so many Emacs features, it was on the list of the things I wanted to explore. Today is the day to do so!
Basic operations with units
The Emacs Calc manual presents a lot of features but makes it hard to
understand how to start. You cannot just enter an expression such as “25mm” in
calc-mode: 25 will be correctly interpreted as a number, but “mm” will be
used to trigger the
m m key binding which executes
what you had in mind.
To enter a value with a unit, use the
' (apostrophe) key to enter an
algebraic expression and hit
return to push it on the stack. You can now use
this value for your computations.
Note that after hitting
', you can use it a second time to recall the
expression on the stack and edit it.
To remove the unit associated with a value. Simply hit
u r, for “unit
Expressions containing multiple units can be simplified. For example, enter
50cm and add them with
+. The resulting expression is
2 m + 50 cm. Hit
u s, for “unit simplify”, to have Calc simplify
Unit conversion is of course possible. Use
u c, for “unit convert”,
at any moment: Calc will ask for a unit and will try to convert the value at
the top of the stack into this unit. For example, entering
the unit for miles) and typing
u c km <return> will correctly yield
You will quickly discover that Calc tries way too hard to convert units and
will produce really strange results when types are not compatible. For
example, trying to convert
250mA into kilometers will produce
is clearly incorrect. I have no idea what causes this behaviour. Fortunately
you can use
u n, bound to
calc-convert-exact-units, which will check that
the unit you required is compatible with the unit of the expression on the
stack. To minimize the risk of producing incorrect results by mistake, I
calc-convert-units function by
would prefer to remap
u c, but Calc has a strange way to handle key bindings
that prevent doing this kind of remapping.
(use-package calc :config (require 'calc-units) (setf (symbol-function 'calc-convert-units) (symbol-function 'calc-convert-exact-units)))
As a shortcut,
u b, where
b stands for “base”, will convert the value
to its base unit. For example, it will automatically convert
Defining custom units
Calc comes by default with more than 170 units (type
u v to list them all),
but none of them are about standard data size units. This is disappointing
given that Emacs is a software primarily used for programming. But we can add
new units, so it is not that much of a problem.
New units can be defined with the
math-additional-units variable. Its format
is the same as
math-standard-units: each entry is a list of three elements:
- A symbol identifying the unit.
- An expression indicating the value of the unit, or
nilfor fundamental units.
- A textual description.
Let us add data sizes. We need two units, bits and bytes, with bytes being
defined as 8 bits. Note that using the
b symbol for the bit unit will
override the internal “Barn” unit; not a problem to me, I do not expect to use
it any time soon. Feel free to use
(setq math-additional-units '((b nil "Bit") (B "8 * b" "Byte"))) (setq math-units-table nil)
We need to set
nil after defining new units to force
Calc to recompute its internal table.
These new units can of course be used with standard SI prefixes defined in
Calc. For example,
25kB will correctly be converted to
25000 B. And asking
for a convertion to the base unit will yield
A limitation of Calc is its inability to handle unit prefixes of more than one
character, so we cannot define
ki as being a prefix with a multiplicative
value of 1024, which would for example allow us to manipulate kibibytes.
A workaround is to define these power-of-two binary units with the prefix included:
(setq math-additional-units '((b nil "Bit") (B "8 * b" "Byte") (kiB "2^10 * B" "Kibibyte") (MiB "2^20 * B" "Mebibyte") (GiB "2^30 * B" "Gibibyte") (TiB "2^40 * B" "Tebibyte") (PiB "2^50 * B" "Pebibyte") (EiB "2^60 * B" "Exbibyte"))) (setq math-units-table nil)
Very helpful for data size conversion.
Going farther with Calc
Writing this post has been an interesting experience: it forced me to read various parts of the Calc manual and some of its source files. It made me realize that this package is full of useful features. I hope to find the time to experiment with Calc features in the future.