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

4
  • 5
    see \strip@pt defined in latex format, you could use that, or to define your own, you need p and t to be catcode 12 not 11 Aug 7, 2013 at 20:14
  • 6
    Perhaps you can (in this and future questions) use a structure were you present the code first, and then explain the problems. This question is really hard to understand in its current form.
    – Mythio
    Aug 7, 2013 at 20:15
  • @John We like to keep questions and answers separate, so I've edited out the answer part you added to the question.
    – Joseph Wright
    Aug 8, 2013 at 14:02

4 Answers 4

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.

2
  • 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 8, 2013 at 7: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 8, 2013 at 7: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.

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.

8
  • There is also printlen.
    – Werner
    Aug 7, 2013 at 20:49
  • Or lengthconvert (which is more accurate than printlen). Aug 7, 2013 at 21:07
  • Just to be picky: the \expandafter before \detokenize is superfluous, because \detokenize looks for an opening brace and expands tokens doing it. But \texttt{\string#1} is easier.
    – egreg
    Aug 7, 2013 at 21:12
  • @egreg Thanks for being picky. I didn't know that. Noted and changed.
    – A.Ellett
    Aug 7, 2013 at 21:18
  • @GonzaloMedina Perhaps you should write up another solution. I feel a bit weird taking other people's suggestions and continually incorporating them into my own answer.
    – A.Ellett
    Aug 7, 2013 at 21:19
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

You must log in to answer this question.

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