Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Is there a way to divide two dimens and get a count? For example, if I have a box that doesn't fit on a single page, and I want to know the number of pages I'd need for the box (i.e., \heightofbox / \textheight), can I get that in a count, or in a macro without any trailing units?

share|improve this question

4 Answers 4

up vote 10 down vote accepted

Calculations can be done with e-TeX's \dimexpr or \numexpr. In the following example, the first expression in \typeout calculates a real number, in the second expression the result is an integer number, rounded up:

\documentclass{article}
\newsavebox{\mybox}

\begin{document}
  \sbox\mybox{\rule{1pt}{3000pt}}
  \makeatletter
  \typeout{\strip@pt\dimexpr 1pt * \ht\mybox / \textheight\relax}
  \typeout{\the\numexpr(\ht\mybox + \textheight/2)/\textheight\relax}
  \makeatother
\end{document}

Result:

5.45454
6

Remarks:

  • In a numerical context (e-)TeX treats dimen or length registers as numbers with unit sp (1pt = 65536sp).
  • \strip@pt removes the unit pt from the result of \dimexpr.
  • The e-TeX \...expr commands round the result. In your case you probably want ceiling. This is achieved by adding the half of the divisor.
  • The expressions are expandable, thus it is easy to use them in counter assignments or inside \edef.

But these numbers should be taken with care:

  • If the box contains something large, that is not breakable, then result would be an overfull \vbox instead of the calculated pages, reducing the count of pages.
  • The box might contain stretchable glue that might occupy more space than calculated with the natural box height. There can be more pages than calculated.

For a more accurate calculation, the box could be split using \vsplit to get the number of pages needed. Depending on the application the number of pages could also be calculated by using page references.

share|improve this answer

You can directly truncate the division by the trick \counter=\length trick which would multiply the length (in pt) by 65536 to get an integer number of sp units (which egreg taught me). Then you can divide and obtain a truncated result.

\documentclass{article}
\newcount\mycounterA
\newcount\mycounterB

\mycounterA=\textheight\relax
\mycounterB=\dimexpr100cm\relax

\begin{document}

\the\mycounterA\par
\the\mycounterB\par

The result is : \divide\mycounterB by\mycounterA\the\mycounterB\par
\advance\mycounterB by 1%
Pages needed : \the\mycounterB

\end{document}

enter image description here

share|improve this answer
    
One big problem is that you will get 6 pages also if the length to be divided fits exactly on 5 pages. So I would subtract 1 from \mycounterB before dividing. –  Stephan Lehmke Dec 6 '12 at 4:26
    
@StephanLehmke Subtracting 1 is the right thing to do. Of course, the chances that the division is exact are minuscule, but it can happen. –  egreg Dec 6 '12 at 7:42

The fp package can manage floating point operations, while \strip@pt is a core-macro that strips the dimension from a length. Here's a small example:

enter image description here

\documentclass{article}
\usepackage[nomessages]{fp}% http://ctan.org/pkg/fp
\newlength{\lengthA}\newlength{\lengthB}
\begin{document}
\setlength{\lengthA}{20em}
\verb|\lengthA: |\the\lengthA \par
\setlength{\lengthB}{5em}
\verb|\lengthB: |\the\lengthB \par
\bigskip

\makeatletter
\edef\valueA{\strip@pt\lengthA}
\edef\valueB{\strip@pt\lengthB}
\FPeval\result{\valueA/\valueB}
\verb|\lengthA/\lengthB: |\result
\end{document}
share|improve this answer

You can convert the dimensions to counters, wheras their division results to an truncated integer, but a simple division will give you problematic results. The division is: How often does q (\textwidth) go in p (\heightoftext)?
The mathematical formula would be ceil(p/q) (rounding up). As p and q are integer values (counters in TeX) we can implement the ceiling function with a little trick.
Let’s take a look at q = 4, p = 2, …, 9:

       p             │  9  8  7  6  5  4  3  │
─────────────────────┼───────────────────────┼────────────────────────────────────────
  int( p    / q)     │  2  2  1  1  1  1  0  │  what we get
 ceil( p    / q)     │  3  2  2  2  2  1  1  │  what we want
  int( p    / q) + 1 │  3  3  2  2  2  2  1  │  what works for p/q ≠ int(p/q)
  int((p–1) / q) + 1 │  3  2  2  2  2  1  1  │  = ceil(p/q)

The second last row needs to be shifted by one to get the actual ceiling function what we achieve by subtracting 1 from q.

\def\divideMeCount#1#2{%
    \countp=#1\relax%
    \advance\countp by -1\relax%
    \countq=#2\relax%
    \divide\countp by \countq\relax%
}

Another solution uses a loop effectively counting the pages:

\def\divideMeLoop#1#2{%
    \dimenp=#1\relax%
    \dimenq=#2\relax%
    \tempi=0\relax%
    \loop\advance\dimenp by -\dimenq\relax\advance\tempi by 1\relax%
    \ifdim\dimenp>0pt\relax\repeat%
}

Values

  • \textheight = 550.0pt
  • \heightofbox =
    • 1099pt
    • 1100pt
    • 1101pt

Code

\documentclass{article}
\newcount\countp
\newcount\countq
\newdimen\dimenp
\newdimen\dimenq
\newcount\tempi
\newdimen\heightofbox
\def\divideMeCount#1#2{%
    \countp=#1\relax%
    \advance\countp by -1\relax%
    \countq=#2\relax%
    \divide\countp by \countq\relax%
}
\def\divideMeLoop#1#2{%
    \dimenp=#1\relax%
    \dimenq=#2\relax%
    \tempi=0\relax%
    \loop\advance\dimenp by -\dimenq\relax\advance\tempi by 1\relax%
    \ifdim\dimenp>0pt\relax\repeat%
}
\usepackage{pgf}
\begin{document}
\heightofbox=1099pt\relax
\the\heightofbox/\the\textheight\par
{\bfseries Counters\par}
\divideMeCount{\heightofbox}{\textheight}
counpt+1=\number\numexpr\countp+1\relax\par
{\bfseries Dimensions (Loop)\par}
\divideMeLoop{\heightofbox}{\textheight}
tempi=\the\tempi\par
{\bfseries PGF (example)\par}
\pgfmathtruncatemacro{\result}{ceil(\heightofbox/\textheight)}
result=\result
\bigskip

\heightofbox=1100pt\relax
\the\heightofbox/\the\textheight\par
{\bfseries Counters\par}
\divideMeCount{\heightofbox}{\textheight}
counpt+1=\number\numexpr\countp+1\relax\par
{\bfseries Dimensions (Loop)\par}
\divideMeLoop{\heightofbox}{\textheight}
tempi=\the\tempi\par
{\bfseries PGF (example)\par}
\pgfmathtruncatemacro{\result}{ceil(\heightofbox/\textheight)}
result=\result
\bigskip

\heightofbox=1101pt\relax
\the\heightofbox/\the\textheight\par
{\bfseries Counters\par}
\divideMeCount{\heightofbox}{\textheight}
counpt+1=\number\numexpr\countp+1\relax\par
{\bfseries Dimensions (Loop)\par}
\divideMeLoop{\heightofbox}{\textheight}
tempi=\the\tempi\par
{\bfseries PGF (example)\par}
\pgfmathtruncatemacro{\result}{ceil(\heightofbox/\textheight)}
result=\result
\end{document}

Output

enter image description here

share|improve this answer
    
@StephanLehmke Yes, it is easier. I haven’t thought of that. Apparently, the error of that calculation is smaller than the actual dimension precision. –  Qrrbrbirlbel Dec 6 '12 at 4:47
    
I think your explanation of the correction by subtracting 1 is wrong (or at least misleading). You want to know how many chunks of size c you need to contain a quantity q. And you want to calculate this with the formula q/c+1. This is simply wrong, because the result is off by 1 if q is a multiple of c. –  Stephan Lehmke Dec 6 '12 at 4:58
    
@StephanLehmke What I would have done initially is q/c + 1 if q/c ≠ int(q/c), q/c otherwise (because the result of a counter division q/c will be floored); this is what I have expressed also in the PGF formula, as we actually want to calculate ceil(q/c). I didn’t want to express these conditions in TeX, so I used the looping method. What we do now is (65536 q - 1)/(65536 c), hardly more correct (mathematically)‽ –  Qrrbrbirlbel Dec 6 '12 at 5:09
    
No the counters are the exact lengths (expressed in sp), so the calculation is correct. It's an illusion that lengths are somewhat "continuous" in TeX, a length register is the same as a count register just with a different "API". –  Stephan Lehmke Dec 6 '12 at 5:46
    
@StephanLehmke Be that as it may. I still need to add 1 to the result because my division’s result will be truncated. But I would agree that there’s no error. –  Qrrbrbirlbel Dec 6 '12 at 6:00

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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