9
\documentclass{article}
\begin{document}
\tracingmacros=2
\newlength{\len} \setlength{\len}{1cm}  we want to write out \verb$\len$ in cm

\the\len { oops, answer in pt}

\setlength{\len}{0.0352\len}
\the\len { good, now we just need to replace pt with cm}

\def\num#1pt{#1cm}
\num1pt { so far so good}

\def\temp{1pt}
\num\temp { not so hot} 0pt %0pt added to end of line just to prevent error message

\expandafter\num\temp   { that works}

\expandafter\num\the\len { but not that} 0pt

\edef\temp{\the\len}
\expandafter\num\temp { okay I'm stuck} 0pt

\makeatletter
\strip@pt\len cm Thank you, that works great!
\makeatother

\end{document}

The document IS the question.

If you want to know what I wanted this for, check out \whereami in http://www.elfsoft2000.com/projects/speaker.pdf

John Kormylo
  • 79,712
  • 3
  • 50
  • 120

4 Answers4

8

The answer by A.Ellett explains how to get what you want, but it's also worth understanding what is wrong with the approach

\def\num#1pt{#1cm}

The first thing to note is that \the is expandable, which you can confirm inside an \edef

\newdimen\mydimen
\edef\demo{\the\mydimen}
\show\demo

gives

> \demo=macro:
->0.0pt.

What is important here, though, is that pt in this is made up of two 'other' (catcode 12) characters, not to 'letters'. Thus to strip off the letters we need to use the correct category code for p and t. The LaTeX kernel defines \rem@pt to do this using

\begingroup
  \catcode`P=12
  \catcode`T=12
  \lowercase{
    \def\x{\def\rem@pt##1.##2PT{##1\ifnum##2>\z@.##2\fi}}}
  \expandafter\endgroup\x

The idea of this is that P and T are made into 'other' characters, and used to crate lower case 'other' p and t. A perhaps clearer definition can be set up using e-TeX:

\makeatletter
\edef\@tempa{%
  \def\noexpand\rem@pt##1\detokenize{pt}{##1}%
}
\@tempa

where I'm using \detokenize to turn p and t into 'other' characters without all of the lower case business.

LaTeX provides \strip@pt as a wrapper around \rem@pt, which can then be used as A.Ellett has demonstrated.

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • The detour via the uppercase letters is only necessary to still be able to use the lowercase letters in command names (e.g. for \rem@pt), right? – cgnieder Aug 08 '13 at 07:56
  • @cgnieder Yes: if we were not using p and t within the code block, we'd just change their catcodes directly. – Joseph Wright Aug 08 '13 at 07:57
7

This answer deals with the precision of the conversion of pt to cm.

Package printlen

Package printlen uses simple TeX arithmetic using a factor 0.0351459 in front of a dimen register.

e-TeX's \dimexpr

Much more precise is e-TeX's \dimexpr, because it uses a 64-bit product as intermediate value, if a multiplication is followed by a division. The result is rounded to 1/65536, because the smallest unit in TeX is 1sp.

Package fp

Fixed point arithmetic is provided by package fp. It supports numbers from -999999999999999999.999999999999999999 to +999999999999999999.999999999999999999. It is the most precise method. The number of digits after the period can be reduced, because TeX smallest unit is 1sp. Therefore more than seven digits do not make much sense.

Other packages

  • LaTeX3 has a module for fixed point arithmetic.
  • pgf/TikZ comes with several methods:
    • \pgfmathparse, ...
    • Fixed Point Arithmetic Library
    • Floating Point Unit Library

Comparison

\documentclass{article}
\usepackage[hmargin=1pt]{geometry}

\makeatletter

\usepackage{printlen}
\newcommand*{\PTtoCMprintlen}[1]{%
  \begingroup
    \uselengthunit{cm}%
    \dimen@=#1\relax
    \printlength{\dimen@}%
  \endgroup
}

\newcommand*{\PTtoCMdimexpr}[1]{%
  \strip@pt\dimexpr(#1)*254/7227\relax cm%
}

\usepackage{fp}
\newcommand*{\PTtoCMfp}[1]{%
  \begingroup
    \dimen@=#1\relax
    \FPset{\PTtoCM@tmp}{\number\dimen@}%
    \FPmul{\PTtoCM@tmp}{\PTtoCM@tmp}{2.54}%
    \FPdiv{\PTtoCM@tmp}{\PTtoCM@tmp}{4736286.72}% 72.27 * 65536
    \FPprint{\PTtoCM@tmp}%
    cm%
  \endgroup
}

\newcommand*{\PTtoCMfpround}[1]{%
  \begingroup
    \dimen@=#1\relax
    \FPset{\PTtoCM@tmp}{\number\dimen@}%
    \FPmul{\PTtoCM@tmp}{\PTtoCM@tmp}{2.54}%
    \FPdiv{\PTtoCM@tmp}{\PTtoCM@tmp}{4736286.72}% 72.27 * 65536
    \FPround{\PTtoCM@tmp}{\PTtoCM@tmp}{7}%
    \FPprint{\PTtoCM@tmp}%
    cm%
  \endgroup
}

\makeatother

\begin{document}
\newcommand*{\test}[1]{%
  #1 & \PTtoCMprintlen{#1} & \PTtoCMdimexpr{#1}
     & \PTtoCMfp{#1} & \PTtoCMfpround{#1} \\%  
}
\begin{tabular}{lllll}
  & printlen & dimexpr & fp & fpround \\
  \hline
  \test{1cm}
  \test{\the\maxdimen}
  \test{89.3999pt}
  \test{1sp}
\end{tabular}
\end{document}

Result

Remarks:

  • If 1cm is assigned to a length, it is rounded to the unit sp, thus the "exact" result is not 1cm, thus the result of package fp is more correct.

  • The number of maximal digits TeX prints in case of its lengths or e-TeX's \dimexpr is optimized for the smallest unit 1sp. After the number is converted from pt to the larger unit cm, the number becomes to small to be printed and the result is rounded to zero.

Moriambar
  • 11,466
Heiko Oberdiek
  • 271,626
6

If you're trying to convert LaTeX internal lengths, which are stored in pt, to a more human readable format, then you can try something like:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\makeatletter
\newcommand{\pttocm}[1]{\texttt{\string#1}=\the#1 is approximately \strip@pt\dimexpr0.03514598#1\relax cm}
\makeatother

\begin{document}

\newlength{\triallen}
\setlength{\triallen}{89.3999pt}

\pttocm{\triallen}

\end{document}

enter image description here

Or, as @werner has pointed out, you can use the package printlen and then

\uselengthunit{cm}
\printlength{\triallen}

Regarding the lost space in your code: that's because a bare LaTeX macro (that is, one not followed by brackets) gobbles up the following space.

A.Ellett
  • 50,533
5

One can use the lengthconvert package or directly a LaTeX3 function; the first way is much more customizable.

\documentclass{article}
\usepackage{lengthconvert}
\newlength{\len}

\begin{document}
\setlength{\len}{1cm}
\Convert[unit=cm]{\len}

\addtolength{\len}{2.5cm}
\Convert[unit=cm]{\len}

\Convert[unit=cm]{1in}

\Convert[unit=pt,precision=2]{1in}

\ExplSyntaxOn
\dim_to_unit:nn {1in}{1mm}
\ExplSyntaxOff
\end{document}

enter image description here

egreg
  • 1,121,712