Format decimal(i) numbers

Tell Metapost to use better precision (see “3.9 Instances” in the Metafun manual).

\startMPpage[instance=decimalfun]
numeric u;
path yy;
u:= 3cm;

for i=0 step 2/10 until 2:
  draw (left--right) shifted (0,i*u); label.lft(textext(decimal(i)), (-2,i*u));
endfor;

yy = (down-- 2 up) scaled u;
draw yy;
\stopMPpage

enter image description here


Nothing is wrong with your code, per se. The issue is that (by default) MetaPost uses a binary number system (scaled point arithmetic in fact, much like TeX). The MetaPost users manual, appendix A, describes it as follows.

The scaled number system refers to 32 bit fixed-point arithmetics described in Section 6.1.

A familiar issue common to all binary number systems is storing certain decimal values exactly. Quoting from the same section:

The point is that with base 2 [...] numbers some decimal numbers cannot be represented exactly, among them such strange numbers like 0.1. In a base 2 [...] number format, this value has an infinit [sic] repeating representation, which cannot be stored in a mantissa of finite precision without introducing a rounding error.

Fortunately, one of the interesting things about MetaPost is that we can pick and choose between a couple of different number systems. They are:

  • scaled (32 bit binary, scaled point; the default),
  • double (64-bit binary, floating point),
  • binary (variable precision binary, floating point),
  • decimal (variable precision decimal, floating point).

You certainly pay a price in performance for using decimal, but in exchange you can exactly represent values such as 0.2. And practically speaking, you may not notice the difference amidst everything else TeX is doing.

So, what about telling ConTeXt which number system to use. You can tell ConTeXt to use a different MetaPost "instance" which is already configured to use a specific number system. There are various pre-defined instances; for our purposes, we mention four of them:

  • metafun (the default instance) uses scaled,
  • doublefun uses double,
  • binaryfun uses binary,
  • decimalfun uses decimal.

The MPcode environment takes an optional argument to tell it which instance to use. Thus, changing

\startMPcode
  ...
\stopMPcode

to

\startMPcode{decimalfun}
  ...
\stopMPcode

will run the code with the decimalfun instance and thereby "fix" your code.

Another possibility is to say \setupMPinstance[method=<method>] before your code, where <method> is one of values scaled, double, binary, and decimal. Then all the instances will use that number system. In particular, changing your code to

\setupMPinstance[method=decimal]

\startMPcode
  ...
\stopMPcode

will also resolve the issue.


Metapost is not very good at additions with floating point numbers; it's better at divisions by 10, though.

\documentclass{article}
\usepackage{luamplib}

\begin{document}

\begin{mplibcode}
beginfig(1);
numeric u;
path yy;
u:= 3cm;
for i=0 step 2 until 20:
  draw (left--right) shifted (0,i*u/10); label.lft(textext(decimal(i/10)), (-2,i*u/10));
endfor;
yy = (down-- 2 up) scaled u;
draw yy;
endfig;
\end{mplibcode}
\mplibnumbersystem{decimal}
\begin{mplibcode}
beginfig(2);
numeric u;
path yy;
u:= 3cm;
for i=0 step 2/10 until 2:
  draw (left--right) shifted (0,i*u); label.lft(textext(decimal(i)), (-2,i*u));
endfor;
yy = (down-- 2 up) scaled u;
draw yy;
endfig;
\end{mplibcode}

\end{document}

(Sorry, no ConTeXt for me). How you pass the decimal number system in ConTeXt I don't know, but anyway it's just an example.

enter image description here