285

How can one determine the length of the different units (measured 1ex, 1em, 1in, 1pt, 1bp, 1dd, 1pc) in mm?

0

9 Answers 9

303

Here is a variant on Herbert's answer, using \dimexpr instead (inspired from the thread \ifnum for real numbers of comp.text.tex), which allows to do conversions in a purely expandable way. The syntax is \convertto{mm}{1pt} to convert 1pt in mm:

\makeatletter
\def\convertto#1#2{\strip@pt\dimexpr #2*65536/\number\dimexpr 1#1}
\makeatother

The results are not quite the same as with the printlen package, probably due to the fact that \dimexpr performs arithmetic slightly differently from TeX. Here's a table showing all the converted lengths (I omitted sp to avoid arithmetic overflows):

\documentclass[a4paper]{article}

\usepackage{array}
\usepackage[hmargin=2cm]{geometry}

\makeatletter
%http://groups.google.com/group/comp.text.tex/msg/7e812e5d6e67fcc5
\def\convertto#1#2{\strip@pt\dimexpr #2*65536/\number\dimexpr 1#1}
\makeatother

\begin{document}

\begin{center}\begin{tabular}
  {>{\def\colunit{pt}}l<{\convertto{\rowunit}{1\colunit}}
   >{\def\colunit{mm}}l<{\convertto{\rowunit}{1\colunit}}
   >{\def\colunit{cm}}l<{\convertto{\rowunit}{1\colunit}}
   >{\def\colunit{ex}}l<{\convertto{\rowunit}{1\colunit}}
   >{\def\colunit{em}}l<{\convertto{\rowunit}{1\colunit}}
   >{\def\colunit{bp}}l<{\convertto{\rowunit}{1\colunit}}
   >{\def\colunit{dd}}l<{\convertto{\rowunit}{1\colunit}}
   >{\def\colunit{pc}}l<{\convertto{\rowunit}{1\colunit}}
   >{\def\colunit{in}}l<{\convertto{\rowunit}{1\colunit}}
   >{\bfseries}l}
\multicolumn{1}{l}{\bfseries 1pt} & \multicolumn{1}{l}{\bfseries 1mm} & \multicolumn{1}{l}{\bfseries 1cm} & \multicolumn{1}{l}{\bfseries 1ex} & \multicolumn{1}{l}{\bfseries 1em} & \multicolumn{1}{l}{\bfseries 1bp} & \multicolumn{1}{l}{\bfseries 1dd} & \multicolumn{1}{l}{\bfseries 1pc} & \multicolumn{1}{l}{\bfseries 1in} & \\
\gdef\rowunit{pt} & & & & & & & & & \rowunit\\
\gdef\rowunit{mm} & & & & & & & & & \rowunit\\
\gdef\rowunit{cm} & & & & & & & & & \rowunit\\
\gdef\rowunit{ex} & & & & & & & & & \rowunit\\
\gdef\rowunit{em} & & & & & & & & & \rowunit\\
\gdef\rowunit{bp} & & & & & & & & & \rowunit\\
\gdef\rowunit{dd} & & & & & & & & & \rowunit\\
\gdef\rowunit{pc} & & & & & & & & & \rowunit\\
\gdef\rowunit{in} & & & & & & & & & \rowunit\\
\end{tabular}\end{center}

\end{document}
4
  • Any particular reason why you do not do: \def\convertto#1#2{\strip@pt\dimexpr #2*65536/\number\dimexpr 1#1\ #1} or \def\convertto#1#2{\strip@pt\dimexpr #2*65536/\number\dimexpr 1#1\relax\ #1} to include the unit in the expanded output? Oct 20, 2016 at 10:58
  • 1
    Of course, the results for ex and em depend on the current font .. Nov 18, 2019 at 22:16
  • It's interesting to note that TeX adopts a quasi-decimal metric system.
    – gboffi
    Jul 3, 2020 at 15:40
  • @gboffi It's a consequence of the internal representation of dimensions in TeX. Everything is, at its lowest level an integer (portable floating point arithmetic wasn't standardized until 1985, well after TeX82's release, so Knuth had to implement his algorithms ex nihilo) and dimensions are represented in integer quantities of sp = 1/65536pt. This, plus the conversions to decimal end up with mild rounding errors that can show up in anything other than the conversions between sp, pt and pc (and possibly ex/em depending on their values)
    – Don Hosek
    Jul 30, 2021 at 13:34
59

incipit (2017)

The precise rules how TeX inputs a dimension expressed in a certain unit are commented in these two posts:

The underlying thing is that internally all dimensions are integer multiples of sp unit, hence there must be some conversion done. A few salient points:

  • the set of those internal dimensions obtainable from a specification in cm has non empty symmetric difference with the set of those dimensions obtainable from a dimension with in as unit. This is important if one intents to use \ifdim tests. This is a core TeX aspect and will not be changed by any higher level interface for testing dimension equality.

  • when expressing a dimension in a given unit <unit>, the granularity is 1/65536<unit> and using more than five digits after the decimal mark can bring only a one-shot change. But it is hard to guess the borderline: nothing can change beyond 17 decimal digits, but the result obtained from 17 decimal digits is not necessarily the same as the one obtained from rounding these 17 decimal digits to only 5 decimal digits. As this might surprise people I am providing here some examples you can try out:

    • 0.22222pt (from 2/9) gives 14563sp but 0.22222222222222222pt gives 14564sp. One must use 0.22223pt to get also 14654sp.

    • 1.53333pt (from 23/15) gives 100488sp but 1.53333333333333333pt gives 100489sp. One must use 1.53334pt to get also 100489sp.

    Yes, using 0.66667pt is the correct choice, but this is a bit an accidental fact as the two examples above show: sometimes one should round in the opposite direction to get what the full specification with 17digits would have given. (unfortunately it appears I have lost a file where I had tested these things but I remember that my testing revealed that counter-intuitive rounding was needed in roughly 10% of the tested cases, built-up using small fractions like the above ones).

The bigger the dimension unit, the less precise the inner representation. Hence using in is a less precise input method than using cm which is less precise than using pt. For example using em typically means you ave a 10sp granularity, and there is no way to go below that, even with using 17 decimal digits in the expression of the dimension using with em unit. Using pt ensures the minimal 1sp granularity.

Regarding the Didot point, there is a more-detailed examination in a section of the xint manual.

(end of 2017 added contents)


Here is another type of table. It gives the exact irreducible conversion factors between units (em and ex are the special font dependent cases; for them I use \dimexpr but this seems not to be a too good idea, it would perhaps be better to get them from the the suitable \fontdimen parameters).

After the exact table I also give a table with values rounded to five decimal places.

Update: Following a suggestion done in a comment, the tables first. There seems to be something fishy with the em: it seems to be exactly 10pt+1sp and not the more intuitively reasonable 10pt. I would have guessed it should be exactly 10pt in that case with the default CM fonts. But I just tested with \the\fontdimen6\font and it also gave to my surprise 10.00002pt, (compiled with pdftex).

Update: before turning off the internet for a while, I checked the em for bold, slanted and teletype: respectively 11.5pt-4sp, 10pt+1sp and 10.5pt-7sp. Perhaps some font expert could explain what is the mechanism? perhaps an underlying conversion from big points at some stage in the font creation process?

Update: I have played a bit with continued fractions, and a good approximation to 1dd is 107/100pt which is clear also from the decimal expansion in the second table, and a very good fraction approximation to 1dd in millimeters is 44/117 (the previous centered convergent is 3/8=0.375 which is already quite good compared to the exact 0.3760650274... value of 1dd in mm).

Exact conversion table

Rounded to 5 places

\documentclass{article}

\usepackage{xintfrac} 
% http://www.ctan.org/tex-archive/macros/generic/xint
% This code was compiled with the version 1.06a of the xint
% bundle dated 2013/05/09, which should appear soon on CTAN.
% The current CTAN version 1.06 should be also OK for this.

\usepackage{array}
\usepackage[hmargin=.5cm]{geometry}

% Conversions to the basic dimension, chosen to be the centimeter
% Base dimension: 1cm
\def\onecm {1}
\def\onemm {1/10}
\def\onein {2.54}
%% \def\onept {\xintMul {1/72.27}{\onein}}
%% simpler:
\def\onept {2.54/72.27}
%% \def\onebp {\xintMul {1/72}{\onein}}
%% simpler:
\def\onebp {2.54/72}
\def\onepc {\xintMul {12}{\onept}}
\def\oneex {\xintMul {\the\numexpr\dimexpr 1ex\relax\relax}{\onesp}}
\def\oneem {\xintMul {\the\numexpr\dimexpr 1em\relax\relax}{\onesp}} 
\def\onedd {\xintMul {1238/1157}{\onept}} 
% 1157 dd = 1238 pt I take this conversion factor from the TeXBook 
% Wikipedia has other conversion factors, but of course here we
% have to do it the TeX way.
\def\onecc {\xintMul {12}{\onedd}}
\def\onesp {\xintMul {1/65536}{\onept}}


% Routines with delimited arguments, the good old TeX way
% (completely expandable)
\makeatletter

% exact conversion to an irreducible fraction:
% example: \convertexactly 126.2772pt\to {bp}
% and `pt' may be a macro expanding to it.
% idem for {bp} which may be a macro expanding to bp
\def\convertexactly #1\to #2%
    {\xintIrr{\convertexactly@ #1\to {#2}}}%

% Variant with rounding at a number of decimal places
% given by first argument.
\def\convertwithrounding #1#2\to #3%
    {\xintRound {#1}{\convertexactly@ #2\to {#3}}}%

% routines doing the job:

\def\convertexactly@ #1\to
{%
    \romannumeral0%
    \expandafter\expandafter\expandafter
    \convertexactly@a
    \xintReverseOrder {#1}\Z
}%
\def\convertexactly@a #1%
{%
    \ifcat\noexpand #1\relax
       \expandafter \convertexactly@b
    \else
       \expandafter \convertexactly@c
    \fi #1%
}%
\def\convertexactly@b #1#2\Z #3%
{%
    \xintdiv {\xintMul {\xintReverseOrder{#2}}{\csname one#1\endcsname}}
             {\csname one#3\endcsname}%
}%
\def\convertexactly@c #1#2#3\Z #4%
{%
    \xintdiv {\xintMul {\xintReverseOrder{#3}}{\csname one#2#1\endcsname}}
             {\csname one#4\endcsname}%
}%

\makeatother

\def\bigstrut {\vbox to 24pt{}\vbox to 12pt{}}%

\begin{document}

Testing:

72.27pt is exactly \convertexactly 72.27pt\to {bp}bp

1/2.54in is exactly \convertexactly 1/2.54in\to {mm}mm

10pt is exactly (for this font) \convertexactly 10pt\to {ex}ex, 
or approximately \convertwithrounding{20}10pt\to {ex}ex

10pt is exactly (for this font) \convertexactly 10pt\to {em}em, 
or approximately \convertwithrounding{20}10pt\to {em}em

1em is exactly (for this font) \convertexactly
1em\to {pt}pt, or approximately
\convertwithrounding{20}1em\to {pt}pt. 

And indeed
\verb+\the\dimexpr 1em\relax+ gives \the\dimexpr 1em\relax{} and
\verb+\the\fontdimen6\font+ gives \the\fontdimen6\font

1ex is exactly (for this font) \convertexactly 1ex\to {pt}pt, or approximately
\convertwithrounding{20}1ex\to {pt}pt. 

And indeed
\verb+\the\dimexpr 1ex\relax+ gives \the\dimexpr 1ex\relax.

\def\tableentry{$\displaystyle\xintFrac{\convertexactly 1\colunit\to\rowunit}$\bigstrut}

\begin{center}\begin{tabular}
  {>{\def\colunit{pt}}l<{\tableentry}
   >{\def\colunit{mm}}l<{\tableentry}
   >{\def\colunit{cm}}l<{\tableentry}
   >{\def\colunit{ex}}l<{\tableentry}
   >{\def\colunit{em}}l<{\tableentry}
   >{\def\colunit{bp}}l<{\tableentry}
   >{\def\colunit{dd}}l<{\tableentry}
   >{\def\colunit{pc}}l<{\tableentry}
   >{\def\colunit{in}}l<{\tableentry}
   >{\bfseries}l}
\multicolumn{1}{l}{\bfseries 1pt} & \multicolumn{1}{l}{\bfseries 1mm} &
\multicolumn{1}{l}{\bfseries 1cm} & \multicolumn{1}{l}{\bfseries 1ex} &
\multicolumn{1}{l}{\bfseries 1em} & \multicolumn{1}{l}{\bfseries 1bp} &
\multicolumn{1}{l}{\bfseries 1dd} & \multicolumn{1}{l}{\bfseries 1pc} &
\multicolumn{1}{l}{\bfseries 1in} & \\
\gdef\rowunit{pt} & & & & & & & & & \rowunit\\
\gdef\rowunit{mm} & & & & & & & & & \rowunit\\
\gdef\rowunit{cm} & & & & & & & & & \rowunit\\
\gdef\rowunit{ex} & & & & & & & & & \rowunit\\
\gdef\rowunit{em} & & & & & & & & & \rowunit\\
\gdef\rowunit{bp} & & & & & & & & & \rowunit\\
\gdef\rowunit{dd} & & & & & & & & & \rowunit\\
\gdef\rowunit{pc} & & & & & & & & & \rowunit\\
\gdef\rowunit{in} & & & & & & & & & \rowunit\\
\end{tabular}\end{center}

\clearpage

\def\tableentry{\convertwithrounding {5}1\colunit\to\rowunit}

\begin{center}\begin{tabular}
  {>{\def\colunit{pt}}l<{\tableentry}
   >{\def\colunit{mm}}l<{\tableentry}
   >{\def\colunit{cm}}l<{\tableentry}
   >{\def\colunit{ex}}l<{\tableentry}
   >{\def\colunit{em}}l<{\tableentry}
   >{\def\colunit{bp}}l<{\tableentry}
   >{\def\colunit{dd}}l<{\tableentry}
   >{\def\colunit{pc}}l<{\tableentry}
   >{\def\colunit{in}}l<{\tableentry}
   >{\bfseries}l}
\multicolumn{1}{l}{\bfseries 1pt} & \multicolumn{1}{l}{\bfseries 1mm} &
\multicolumn{1}{l}{\bfseries 1cm} & \multicolumn{1}{l}{\bfseries 1ex} &
\multicolumn{1}{l}{\bfseries 1em} & \multicolumn{1}{l}{\bfseries 1bp} &
\multicolumn{1}{l}{\bfseries 1dd} & \multicolumn{1}{l}{\bfseries 1pc} &
\multicolumn{1}{l}{\bfseries 1in} & \\
\gdef\rowunit{pt} & & & & & & & & & \rowunit\\
\gdef\rowunit{mm} & & & & & & & & & \rowunit\\
\gdef\rowunit{cm} & & & & & & & & & \rowunit\\
\gdef\rowunit{ex} & & & & & & & & & \rowunit\\
\gdef\rowunit{em} & & & & & & & & & \rowunit\\
\gdef\rowunit{bp} & & & & & & & & & \rowunit\\
\gdef\rowunit{dd} & & & & & & & & & \rowunit\\
\gdef\rowunit{pc} & & & & & & & & & \rowunit\\
\gdef\rowunit{in} & & & & & & & & & \rowunit\\
\end{tabular}\end{center}

\end{document}

testing the conversion macro

Actually the two approximations, given with 20 places after the decimal mark, for 1em and 1ex, are exact, the denominators are powers of 2, the complete decimal expansion only has zeros after those shown. I still do not quite understand why 1em turns out to be 655361sp and not 655360sp=10pt in the case of the CM font. This 10pt+1sp is strange.

4
  • +1, neat! Two suggestions: Use the parskip package to get some space between the “sentences”. Put the picture first in your answer so that it’s closer to your remarks; unlike in most other answers, the output seems more important than the code here.
    – doncherry
    May 9, 2013 at 21:02
  • Sorry. What is the exact range in pt? Jul 6, 2014 at 9:41
  • @kissmyarmpit sorry for delay. The maximal input for a dimension expressed in pt is 16383.99999237060546874pt. Anything bigger creates a dimension too large error. This corresponds to 1073741823sp (2^30-1). In fact the minimal spec which gives this maximal allowed value is 16383.99997711181640625pt. Anything smaller gives at most 2^30-2 sp.
    – user4686
    Mar 27, 2017 at 8:14
  • @kissmyarmpit using five decimal digits, both 16383.99998pt and 16383.99999pt give the maximal dimension, but not 16383.99997pt. And \the\maxdimen outputs 16383.99998pt. But as I said 16383.99999pt is still acceptable (try \the\dimexpr 16383.99999pt\relax.) but not 16383.999993pt.
    – user4686
    Mar 27, 2017 at 8:22
47

alt text

And TeX's nice arithmetic can be seen .. ;-)

\documentclass{article}
\usepackage{printlen}
\parindent=0pt

\newlength\Length \Length=1cm
\begin{document}

\tabular{p{2cm}p{2cm}}
\mbox{--- 1cm ---}

\uselengthunit{cm}\printlength{\Length}\
\uselengthunit{mm}\printlength{\Length}\
\uselengthunit{in}\printlength{\Length}\
\uselengthunit{pt}\printlength{\Length}\
\uselengthunit{bp}\printlength{\Length}\
\uselengthunit{sp}\printlength{\Length}\
\uselengthunit{pc}\printlength{\Length}\
& 
\mbox{--- 1em ---}
\Length=1em
\uselengthunit{cm}\printlength{\Length}\
\uselengthunit{mm}\printlength{\Length}\
\uselengthunit{in}\printlength{\Length}\
\uselengthunit{pt}\printlength{\Length}\
\uselengthunit{bp}\printlength{\Length}\
\uselengthunit{sp}\printlength{\Length}\
\uselengthunit{pc}\printlength{\Length}
\endtabular
\end{document}
6
  • 10
    Heh! I like how the centimeter didn't come out right...
    – SamB
    Jan 4, 2011 at 19:18
  • 11
    1ex is supposed to be the height of 'x', and 1em is supposed to the width of 'm' -- so they should vary with the font and font size. Do they?
    – frabjous
    Jan 5, 2011 at 2:27
  • 10
    @frabjous: 1em is the width of M and sure they vary
    – user2478
    Jan 5, 2011 at 6:57
  • 4
    Thanks, Herbert. That's an important thing to note for the question asked.
    – frabjous
    Jan 5, 2011 at 19:31
  • 1
    @frabjous: This isn't quite right for fonts with narrow M's, see en.wikipedia.org/wiki/Em_%28typography%29
    – Caramdir
    Jan 5, 2011 at 23:33
24
  • An inch is 25.4 mm.
  • For TeX, 1 pt is 1/72.27 in, which is 0.351459804 mm.
  • For most other software, 1 pt is 1/72 in, which is 0.352777778 mm. Also called Postscript Point, in TeX this is called a big point (bp).
  • ex and em are not a fixed length, as they depend on the fontsize. (See this question.)

Edit: Herberts answer shows you how to find the length of 1 ex and 1em in your document.

7
  • You have a small typo here. 1 TeX point is 1/72.27 inch (not 1/72.72 inch). BTW I think it is not widely known that Google's calculator supports both points. Exemplary searches: 1 TeX point - 1/72.27 inch, 1 PS point - 1/72 inch, both obviously returning 0.
    – przemoc
    Jun 27, 2011 at 20:37
  • @przemoc: Indeed, you're right, thanks. I don't think I'll edit the post though, that'll bring the question to the top of the list again, and the correct value is in the accepted answer anyway. Jun 27, 2011 at 20:54
  • You shouldn't care about such small detail as moving your answer to the top, because keeping the mistake is definitely bad. Moreover, Ctrl+F on 1/72.27 returns only my comment, which is pretty bad, because it is formal definition of point in TeX and should be clearly mentioned somewhere, i.e. in the main text of answer, not in a comment.
    – przemoc
    Jun 27, 2011 at 21:06
  • @przemoc: That is a good point, I'll fix it. Jun 27, 2011 at 22:44
  • 1
    I’ve just recherched point sizes and history, because I have found conflicting information about the Teχ point. It is in fact 1/72.27 of the modern (25.4 mm) inch, or 800/803 of the PostScript point (1/72 inch).
    – mirabilos
    Aug 11, 2015 at 15:38
20

The same as Philippe Goutet's, but using the fp module of LaTeX3; however the syntax of the \convertto command is different:

\convertto{1in}{pt}

returns 72.26999, just the number. With \convertto*{1in}{pt} we'd get 72.26999pt, with the unit.

\documentclass[a4paper]{article}

\usepackage{array}
\usepackage[hmargin=2cm]{geometry}

\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\convertto}{smm}
 {
  \egreg_convertto:nn {#2}{#3}
  \IfBooleanT{#1}{#2}
 }

\cs_new:Npn \egreg_convertto:nn #1 #2
 {
  \fp_eval:n { round( \dim_to_decimal:n { #1 } / \dim_to_decimal:n {1#2} , 5 ) }
 }
\ExplSyntaxOff

\begin{document}

\begin{center}\begin{tabular}
  {>{\def\colunit{pt}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{mm}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{cm}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{ex}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{em}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{bp}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{dd}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{pc}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{in}}l<{\convertto{1\colunit}{\rowunit}}
   >{\bfseries}l}
\multicolumn{1}{l}{\bfseries 1pt} & \multicolumn{1}{l}{\bfseries 1mm} &
\multicolumn{1}{l}{\bfseries 1cm} & \multicolumn{1}{l}{\bfseries 1ex} &
\multicolumn{1}{l}{\bfseries 1em} & \multicolumn{1}{l}{\bfseries 1bp} &
\multicolumn{1}{l}{\bfseries 1dd} & \multicolumn{1}{l}{\bfseries 1pc} &
\multicolumn{1}{l}{\bfseries 1in} & \\
\gdef\rowunit{pt} & & & & & & & & & \rowunit\\
\gdef\rowunit{mm} & & & & & & & & & \rowunit\\
\gdef\rowunit{cm} & & & & & & & & & \rowunit\\
\gdef\rowunit{ex} & & & & & & & & & \rowunit\\
\gdef\rowunit{em} & & & & & & & & & \rowunit\\
\gdef\rowunit{bp} & & & & & & & & & \rowunit\\
\gdef\rowunit{dd} & & & & & & & & & \rowunit\\
\gdef\rowunit{pc} & & & & & & & & & \rowunit\\
\gdef\rowunit{in} & & & & & & & & & \rowunit\\
\end{tabular}\end{center}

\end{document}

Both \convertto and \convertto* can be used in an expandable context.

enter image description here

The same table but with siunitx (with the already built one for comparison). One needs to prepare the body of the table beforehand.

\documentclass[a4paper]{article}

\usepackage{siunitx,array}
\usepackage[hmargin=1cm]{geometry}
\usepackage{xparse}

\ExplSyntaxOn

\seq_new:N \g_egreg_convertto_units_seq
\seq_new:N \l_egreg_convertto_temp_seq
\tl_new:N \l_egreg_convertto_body_tl

\seq_gset_from_clist:Nn \g_egreg_convertto_units_seq
 {
  pt, mm, cm, ex, em,  bp, dd, pc, in
 }

\cs_new_protected:Nn \__egreg_convertto_maketable:
 {
  \seq_map_inline:Nn \g_egreg_convertto_units_seq
   {
    \seq_clear:N \l_egreg_convertto_temp_seq
    \seq_map_inline:Nn \g_egreg_convertto_units_seq
     {
      \seq_put_right:Nx \l_egreg_convertto_temp_seq { \egreg_convertto:nn { 1####1 } { ##1 } }
     }
    \tl_put_right:Nx \l_egreg_convertto_body_tl
     {
      \seq_use:Nn \l_egreg_convertto_temp_seq { & } & \exp_not:n { \textbf{##1} \\ }
     }
   }
  \seq_set_map:NNn \l_egreg_convertto_temp_seq \g_egreg_convertto_units_seq { {\exp_not:N \textbf{1\,##1}} }
 }

\NewDocumentCommand{\maketable}{}
 {
  \__egreg_convertto_maketable:
  \begin{tabular}{*{\seq_count:N \g_egreg_convertto_units_seq}{S[table-format=2.5]}l}
  \seq_use:Nn \l_egreg_convertto_temp_seq { & } \\
  \l_egreg_convertto_body_tl
  \end{tabular}
 }

\NewExpandableDocumentCommand{\convertto}{smm}
 {
  \egreg_convertto:nn {#2}{#3}
  \IfBooleanT{#1}{#2}
 }

\cs_new:Npn \egreg_convertto:nn #1 #2
 {
  \fp_eval:n { round( \dim_to_decimal:n { #1 } / \dim_to_decimal:n {1#2} , 5 ) }
 }
\ExplSyntaxOff

\begin{document}

\begin{center}
\sisetup{group-digits=false}
\maketable
\end{center}

\begin{center}
\begin{tabular}
  {>{\def\colunit{pt}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{mm}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{cm}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{ex}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{em}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{bp}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{dd}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{pc}}l<{\convertto{1\colunit}{\rowunit}}
   >{\def\colunit{in}}l<{\convertto{1\colunit}{\rowunit}}
   >{\bfseries}l}
\multicolumn{1}{l}{\bfseries 1pt} & \multicolumn{1}{l}{\bfseries 1mm} &
\multicolumn{1}{l}{\bfseries 1cm} & \multicolumn{1}{l}{\bfseries 1ex} &
\multicolumn{1}{l}{\bfseries 1em} & \multicolumn{1}{l}{\bfseries 1bp} &
\multicolumn{1}{l}{\bfseries 1dd} & \multicolumn{1}{l}{\bfseries 1pc} &
\multicolumn{1}{l}{\bfseries 1in} & \\
\gdef\rowunit{pt} & & & & & & & & & \rowunit\\
\gdef\rowunit{mm} & & & & & & & & & \rowunit\\
\gdef\rowunit{cm} & & & & & & & & & \rowunit\\
\gdef\rowunit{ex} & & & & & & & & & \rowunit\\
\gdef\rowunit{em} & & & & & & & & & \rowunit\\
\gdef\rowunit{bp} & & & & & & & & & \rowunit\\
\gdef\rowunit{dd} & & & & & & & & & \rowunit\\
\gdef\rowunit{pc} & & & & & & & & & \rowunit\\
\gdef\rowunit{in} & & & & & & & & & \rowunit\\
\end{tabular}\end{center}

\end{document}

enter image description here

8
  • It seems that TeX's memory you used is more important. For Philippe :777 strings out of 493481 10679 string characters out of 3140965 67406 words of memory out of 3000000 For you 7172 strings out of 493481 128969 string characters out of 3140965 174995 words of memory out of 3000000 May 9, 2013 at 8:16
  • 1
    @AlainMatthes Loading expl3 on top of LaTeX2e has a big impact. If I load xparse over Philippe's macros I get 7166 strings out of 493481, 128879 string characters out of 3140966, 175547 words of memory out of 3000000. LaTeX3 will surely consume more memory than LaTeX2e, at the advantage of a cleaner approach.
    – egreg
    May 9, 2013 at 8:52
  • @AlainMatthes There's a reason that the LaTeX3 approach didn't fly at all when it was first developed (early 1990s). As egreg says, there is a cost to having a more structured approach, but the idea is that you enhance code readability and logic.
    – Joseph Wright
    May 10, 2013 at 6:50
  • Not sure what is happening. I copied the code for your answer into a MacTeX2016 (updated a few days ago) TeXShop file. The first row and first column are fine, but all other entries are incorrect. For example, Column 2 Row 2 is 8.09555 and Column 2 Row 9 is 205.62699. Did I neglect something?
    – sgmoye
    Mar 27, 2017 at 20:03
  • @sgmoye Yes, some internals in expl3 have changed. Updated.
    – egreg
    Mar 27, 2017 at 20:21
8

My posting just crossed the other ones. Best to view it, in action.

\documentclass[11pt]{article} % use larger type; default would be 10pt
\usepackage{xcolor}
\begin{document}

\newdimen\temp

\def\alength#1#2{
\temp#1
\nointerlineskip \baselineskip=2pt
\vbox{\hbox{\hskip-29pt\texttt{\textcolor{#2}{#1=\the\temp}}}
\hbox{\vtop{\color{#2}\hrule width 130pt\vspace{#1}\hrule width 130pt}}%
\vspace{0.9cm}}
}

\alength{1cm}{blue}
\alength{1pc}{red}
\alength{1mm}{orange}
\alength{1cc}{red}
\alength{1dd}{red}
\alength{100000sp}{purple}
\alength{1in}{black}
\alength{1bp}{brown}
\alength{1em}{black}
\alength{1ex}{black}
\end{document}
3
  • 1
    It is better if you put all in one base line to make a comparison. Jan 4, 2011 at 17:26
  • @xport The base is the relationship to points! I tried to give something to visualize the length (with the exception of the sp, which is smaller than the visible wavelength and hence I scaled that by100K. Unit conversion tables are normally done like this. Jan 4, 2011 at 18:32
  • Thanks Yiannis Lazarides, but how to calc result in mm? 1pc=x mm 1pt=x mm 1ex=x mm 1em=x mm Jan 5, 2011 at 11:05
5

Some more examples with 1em

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{mathpazo}
\newsavebox\CBox
\newlength\Length \Length=1em
\begin{document}

\sbox\CBox{M}
\the\wd\CBox : \the\Length

\tiny\Length=1em
\sbox\CBox{M}
\the\wd\CBox : \the\Length

\Huge\Length=1em
\sbox\CBox{M}
\the\wd\CBox : \the\Length

\end{document}

alt text

5

As already mentioned in other answers, the e-TeX \numexpr and dimexpr allow easy comparisons of dimensions, through conversions to integers. In particular applying \number to a \dimexpr expression returns the internal representation of the dimension in scaled points: there are 65536 scaled points in one pt. Using this we can see some funny effect of the internal truncations done by TeX:

big points

We see that 100bp are represented in an exact manner internally (exact meaning that the ratio 72.27/72 is exactly verified). The next to last line should read rather 100bp/100pt, and not 1bp/1pt, to illustrate that this exact ratio is obtained only for dimensions which are integer multiples of 100bp. As we can see indeed, it is not possible to represent exactly 10bp as an integer number of sp. We see that truncation, rather than rounding is used. Indeed 10bp would be more accurately represented by 657818sp and 1bp would be more accurately equated to 65782sp.

The code for producing the above:

1bp=\number\dimexpr 1bp\relax sp

10bp=\number\dimexpr 10bp\relax sp

100bp=\number\dimexpr 100bp\relax sp

1000bp=\number\dimexpr 1000bp\relax sp

10000bp=\number\dimexpr 10000bp\relax sp


1pt=\number\dimexpr 1pt\relax sp

10pt=\number\dimexpr 10pt\relax sp

100pt=\number\dimexpr 100pt\relax sp

1000pt=\number\dimexpr 1000pt\relax sp

10000pt=\number\dimexpr 10000pt\relax sp

\bye

The last lines were done with \input xintfrac.sty (package xint ) and

1bp/1pt=\xintIrr{\number\dimexpr 100bp\relax/\number\dimexpr 100pt\relax }

72.27/72=\xintIrr{72.27/72}

\bye

To get the e-TeX extensions the executable etex (or pdftex which is what I actually used) rather than tex must be used.

0

I am not completely satisfied with the macro convertto because I have no control over the number of digits after the decimal separator and I always have to use the decimal point as the decimal separator, as is common in the USA. In many other countries, however, the decimal comma is used, which is made possible by the siunitx package. My macro ConvertTo has an optional parameter for the number of digits after the decimal separator and uses the package fp for calculation.

\documentclass{article}
\usepackage{fp}
\usepackage[locale = DE,detect-all]{siunitx}

\makeatletter
\def\convertto#1#2{\strip@pt\dimexpr #2*65536/\number\dimexpr 1#1}
\makeatother

\newcommand*\ConvertTo[3][2]{%
\FPdiv\res{\number\dimexpr #3}{\number\dimexpr 1#2}\relax%
\FPeval\res{round(\res:#1)}\SI\res{#2}}

\begin{document}
\newdimen\mylength\mylength=5440pt
\convertto{cm}{\mylength}\ cm, \ConvertTo{cm}{\mylength}, 
\ConvertTo[6]{cm}{\mylength}, \ConvertTo[0]{bp}{\mylength}.
\end{document}

Output: 191.19421 cm, 191,19 cm, 191,194 216 cm, 5420 bp.

Remark: If you're a pedantic mathematician like me, you'll notice that the result for cm is only correct up to the third digit after the decimal point. The next macro calculates more precisely, see my answer to How to print a length accurately and with user-controlled rounding?

\newcommand{\ScaledPointsInPoint}{\number \dimexpr 1pt \relax}
\newcommand*{\FPSizeCm}[2][2]{%
\FPdiv\res{\number\dimexpr #2\relax}{\ScaledPointsInPoint}%
\FPdiv\res{\res}{72.27}%
\FPmul\res{2.54}{\res}%
\FPeval\res{round(\res:#1)}%
\SI\res{\cm}}

This macro is more precise, because TeX obviously converts all lengths internally first into points and then into scaled points. So it is better to first convert back from scaled point to point and then to inch, cm, mm, etc. This is more accurate than converting directly into these units as the macros convertto and ConvertTo do.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .