41

This question led to a new package:
smartunits

I am currently writing a document that I would like to deploy to both European and American audiences. The problem is, as usual, the choice between imperial vs metric units.

The text is not scientific, and not meant for a scientific audience, so I cannot simply use metric units all the way through and call it a day. Since many of the units are buried in the text, finding them all and changing them is quite cumbersome (even with the use of hidden comments as guiding flags).

What I am looking for is a method to offer the best out of both worlds. I am looking for something that can convert all metric units into imperial and vice versa. The ideal scheme would look like this:

\usepackage{smart_unit_package}

\smart_unit_convert{imperial}
...

\begin{document}
Oscar Wilde's height is said to have been \smart_unit{1.91}{meter}.
\end{document}

Which should hopefully result in:

Oscar Wilde's height is said to have been 6'3''

The idea behind it would be that the units can be controlled from the preamble, without having to alter the text.

Is there such a package available, or is it easily possible to create my own solution (I'm not that great at coding, and have never even tried writing anything in Tex before)?

I took a look at the siunitx package, but there is nothing in there about converting units back and forth.

Edit: As per Joseph Wright's comment, a few more details: The units to convert would, at the minimum, be for weight, length, and time (with the last one optional, but advantageous). The conversion should be from standard metric (kilograms, meters, 24:00 format) to imperial (pounds, feet, 12 AM/PM format).

If I personally were writing a package, I would probably allow for certain command flags (e.g.: kilometer should translate to miles, not to thousands of feet), and body height (say 189 cm) should convert to 6'2'', not to "2 yards".

I realize that getting this right for every possible scenario (body height, mountain height, small distance, large distance, etc. etc.) is somewhat complex (mostly thanks to the weird parts of the imperial system). As to accuracy: Since it is not written for a scientific audience, any rounding up to the decimal point is fine. I.e.: 1 meter can easily convert into 3 feet, even if it is not really exact. Of course, if more accuracy were needed, a roundoff-point would be necessary.

Second edit: A simple hack that I think might also work (even if it's not really elegant), is a way in latex to define two words. For instance:

Oscar Wilde's height is said to have been \twowords{191cm}{6'3''}.

With some possibility of choosing which one gets selected in the preamble. This would force the user to convert everything by himself, but it would allow easy swapping between units, at least.

  • 3
    Welcome to TeX-sx! Whilst there is not to my knowledge a pre-built package for this it's entirely doable. Can you perhaps tighten up on the requirements, for example the exact units to convert, the nature of the conversions required, etc. (for example, for weights one might do kilograms to stone and pounds or just to pounds). Also, how accurate does this need to be. – Joseph Wright Apr 29 '15 at 8:16
  • 11
    A problem with such an automatic conversion is that rounding is usually made. If someone weighs 76 kg, one would probably translate this into 12 stones, not 11 31/32 stones (or 11 stones 13.55 pounds). Similarly 6'3'' would become 1.90 meters, not the “more correct” 1.9062 meters. It's common in translations from English to translate “200 yards” into “200 meters“ (or “about 200 meters”), unless accuracy is really needed. So, a “200 yard path” would become “un sentiero di circa 200 metri” if I had to do a translation into Italian. – egreg Apr 29 '15 at 10:57
  • 4
    @egreg Unless you're a government... In Canada you find signs like "No smoking within 7.62 m of this building." :) – Alan Munn Apr 29 '15 at 20:18
  • 2
    The precision problem @egreg mentions exists also in an even worse form: IIRC, there was once a book titled "How to make a million dollars out of nothing" or the like, and someone translated that to another language and currency - using a currency conversion ratio with five digit precision! :) – Hagen von Eitzen Apr 29 '15 at 20:54
  • 3
    @HagenvonEitzen “How to make 899 887.514 € out of nothing” – egreg Apr 29 '15 at 20:55
28

Implementing your hack is quite easy:

\newif\ifMetric\Metrictrue% metric by default
\newcommand\MyUnit[2]{\ifMetric #1\else #2\fi}% \MyUnits{metric}{imperial}

Then you can simply use \MyUnit{191cm}{6'3''} in your document and change between metric and imperial at any point using \Metrictrue and \Metricfalse.

Here's a full example (with a crude use of SIunits as requested in the comments):

\documentclass{article}
\usepackage{SIunits}
\newif\ifMetric\Metrictrue% metric by default
\newcommand\MyUnit[2]{\ifMetric #1\else #2\fi}% \MyUnits{metric}{imperial}

\begin{document}
   Metric: \MyUnit{191 \centi\meter}{$6'3''$}

   \Metricfalse Imperial:  \MyUnit{191 \centi\meter}{$6'3''$}

   \Metrictrue Metric:  \MyUnit{191 \centi\meter}{$6'3''$}
\end{document}

and the output:

enter image description here

Edit: the smartunits package

When I first wrote this post I said that it ought to be possible to do this properly using pgfkeys. Partly as a proof-of-concept, and partly as an exercise to learn how to use pgfkeys, there is now a smartunits package for converting between metric and Imperial units.

Here is a MWE:

\documentclass{article}
\usepackage{smartunits}
\usepackage{xcolor}
\usepackage{listings}\lstset{language=[LaTeX]TeX}
\lstset{language=[LaTeX]TeX,
        texcsstyle=*\bfseries\color{blue},
        keywordstyle=\color{blue},
        commentstyle=\color{brown},
        morekeywords={SmartUnit,SmartUnitSettings,sisetup},
}

\begin{document}
  \begin{lstlisting}[texcl]
      \SmartUnitSettings{metric imperial, places=1}
      \SmartUnit{km=100.0,figures=1}   % \SmartUnit{km=100.0,figures=1}
      \SmartUnit{miles=62.15,places=1} % \SmartUnit{miles=62.15,places=1}
      \SmartUnit{cm=10}                % \SmartUnit{cm=10}
      \SmartUnit{celsius=20}           % \SmartUnit{celsius=20}
      \SmartUnit{miles=5.0, figures=1} % \SmartUnit{miles=5.0, figures=1}
      \SmartUnit{miles=5.0,places=2}   % \SmartUnit{miles=5.0, places=2}
      \SmartUnit{hours=0, minutes=59}  % \SmartUnit{hours=0, minutes=59}
      \SmartUnit{hours=12, minutes=12} % \SmartUnit{hours=12, minutes=12}
      \SmartUnit{kg=10.0, places=1}    % \SmartUnit{kg=10.0, places=1}
      \SmartUnit{pound=10.0,figures=1} % \SmartUnit{pound=10.0,figures=1}
      \SmartUnit{l=10.0, places=1}     % \SmartUnit{l=10.0, places=1}
      \SmartUnit{L=10.0, places=1,uk}  % \SmartUnit{L=10.0, places=1,uk}
  \end{lstlisting}
\end{document}

and here is the output this produces:

enter image description here (There is some trickery using the listings package to have LateX typeset the commands after the %'s on each line.)

  • Thanks for the answer! This is already a huge step forward for me (you don't want to know how many units I would have had to manually switch). I'll flag this as the correct answer in the next few days if nobody comes up with the magical bullet package that I hope exists somewhere. – Mark Anderson Apr 29 '15 at 9:58
  • 2
    This hurts my eyes, you should use siunitx for the units or at least put a small space between number and unit \, – MaxNoe Apr 29 '15 at 12:06
  • 1
    :%s/\(\d'\d''\)/$\1$/g for correctly typeset primes instead of the (curly) quotes – wchargin Apr 29 '15 at 14:13
  • That's how I would do it. The next step may be to integrate it with siunitx. – Raphael Apr 29 '15 at 15:02
  • @MarkAnderson I have added a proper "smart" interface. – Andrew May 15 '15 at 8:55
17

For the sake of completeness, one option is to just do the unit conversions in LaTeX, using something like pgf/tikz or fp. I am personally of the opinion that this isn't an ideal solution, since there are a lot of edge cases (like, for instance, when to convert between meters and kilometers, or how much to round). It may be sufficient for a nonscientific use, though. With that caveat out of the way, here's a possible implementation.

First, we set up a flag to select between metric and imperial units.

\newif\ifmetric\metrictrue

We can then start by implementing a macro which switches between 24 hour time and 12 hour time, depending on the state of this flag. We'll assume that we input a time in 24 hour form, and convert it to 12 hour time if necessary.

\def\smarttime#1#2{%
  \ifmetric%
    #1:#2%
  \else%
    \ifnum#1>12%
      \FPeval{\result}{trunc(#1 - 12:0)}%
      \result:#2 PM%
    \else%
      #1:#2 AM%
    \fi%
  \fi%
}

Basically, what this does is check if the first argument (the hours) is greater than 12. If it is, then it uses the fp package's \FPeval to subtract 12 from the number of hours. The time is then that new number of hours, followed by the minutes and PM. Otherwise, we just add an AM to the end of the time. This should actually completely work for your needs.

Next, let's do conversion between two units, say kilograms and pounds.

\def\smartkilogram#1{%
  \ifmetric%
    #1 kg%
  \else%
    \FPeval{\result}{round(#1 * 2.204:1)}%
    \result\ lbs%
  \fi%
}

Again we just do the math using \FPeval and use our flag to switch between metric and imperial units.

Converting between meters and feet plus inches is a little bit more involved, but not fundamentally different from how we did kilograms to pounds.

% Conditionally converts meters to miles/feet/inches
\def\smartmeter#1{%
  % Use metric units (meters)
  \ifmetric%
    \FPeval{\result}{trunc(#1:0)}%
    \ifnum\result>1000%
      \FPeval{\result}{trunc(#1/1000:1)}%
      \result\ km%
    \else%
      \FPeval{\result}{trunc(#1:2)}%
      \result\ m%
    \fi%
  % Use imperial units (feet and inches)
  \else
   \FPeval{\result}{trunc((3.281 * #1):0)}%
   \ifnum\result<5280%
     \FPeval{\result}{(3.281 * #1)}%
     \FPeval{\feet}{trunc((\result):0)}%
     \FPeval{\inch}{trunc((12 * ((\result) - (\feet))):0)}%
     \feet'\inch"%
    \else
     \FPeval{\feet}{trunc((\result):0)}%
     \FPeval{\miles}{trunc((\feet / 5280):1)}%
     \miles\ miles%
   \fi%
  \fi%
}

If you are working with kilometers as well, it might make sense to define something like this:

\def\smartkilometer#1{\FPeval{\result}{trunc((#1 * 1000):0)}\smartmeter{\result}}

What this does is it multiplies whatever number you give it by 1000 and puts that into the \smartmeter macro. It uses the name of the macro (\smartmeter vs \smartkilometer) to keep track of units.

With those macros defined, the following code:

\subsection*{Metric Units}
\metrictrue
Oscar Wilde's height is said to have been \smartmeter{1.91}.\\

Five kilometers is \smartkilometer{5}.\\

It is \smarttime{9}{00}. Later it will be \smarttime{13}{30}.\\

That ostrich weights \smartkilogram{100}!

\subsection*{Imperial Units}
\metricfalse
Oscar Wilde's height is said to have been \smartmeter{1.91}.\\

Five kilometers is \smartkilometer{5}.\\

It is \smarttime{9}{00}. Later it will be \smarttime{13}{30}.\\

That ostrich weights \smartkilogram{100}!

becomes this:

enter image description here

  • 1
    For what you're doing, I'd suggest using at most the time macro, and then instead doing the unit conversions by hand and doing something more like Andrew's solution. This "works," but it feels really messy and probably fails at edge cases like large or small values. – Brian Apr 29 '15 at 19:19
  • 1
    But 100 kg should be a over 200 lbs – Hagen von Eitzen Apr 29 '15 at 21:00
  • ...why yes, it should. I've edited the answer to change the division by 2.204 to a multiplication by 2.204. I got the conversion backwards. – Brian Apr 29 '15 at 21:04
  • That solution looks very nice already! Thanks for taking the time to write it all! May I add a small addendum? Since it is available, I suggest using the SIUnitX package for the metric units. This is makes it easier to typeset everything. – Mark Anderson Apr 30 '15 at 19:47
  • I had never seen SIUnitX before. It does look like a better way to do the units. I believe that all that would be needed would be to replace the lines that look like \result km with lines like \SI{\result}{\kilo\meter}. – Brian May 1 '15 at 1:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.