summaryrefslogtreecommitdiff
path: root/macros/generic/texdimens/texdimens.tex
diff options
context:
space:
mode:
Diffstat (limited to 'macros/generic/texdimens/texdimens.tex')
-rw-r--r--macros/generic/texdimens/texdimens.tex137
1 files changed, 110 insertions, 27 deletions
diff --git a/macros/generic/texdimens/texdimens.tex b/macros/generic/texdimens/texdimens.tex
index d8bf99e0bc..61086d138d 100644
--- a/macros/generic/texdimens/texdimens.tex
+++ b/macros/generic/texdimens/texdimens.tex
@@ -1,18 +1,18 @@
% This is file texdimens.tex, part of texdimens package, which
% is distributed under the LPPL 1.3c. Copyright (c) 2021 Jean-François Burnol
-% 2021/11/02 v0.99
-% All macros from 0.9delta release have changed names: \texdimen prefix
-% has replaced \texdimin.
-\edef\texdimensendinput{\endlinechar\the\endlinechar\catcode`\noexpand _=\the\catcode`\_\relax\noexpand\endinput}%
+% 2021/11/04 v0.99d
+\edef\texdimensendinput{\endlinechar\the\endlinechar%
+\catcode`\noexpand _=\the\catcode`\_%
+\catcode`\noexpand @=\the\catcode`\@\relax\noexpand\endinput}%
\endlinechar13\relax%
-\catcode`\_=11
+\catcode`\_=11 \catcode`\@=11 % only for using \p@ of Plain. Check exists?
%
% Mathematics ("down" and "up" macros)
% ===========
%
% In the entire discussion here, "uu" stands for some core unit,
% or some unit corresponding to a dimension > 1pt. For the case
-% of a unit corresponding to a dimension < 1pt, i.e. to
+% of a unit corresponding to a dimension < 1pt, i.e. to
% \texdimenwithunit macro added at 0.99, refer to the
% comments of issue #2 on the tracker site.
%
@@ -378,39 +378,122 @@
% Mathematics
% ===========
%
-% <comments to be added> (see discussion #2)
-%
+% As explained in the README.md, the ex and em units are
+% handled by TeX as if multiplying by a conversion factor f/65536
+% (here f sp = 1ex resp. = 1em).
+% In particular, for any decimal D, input "D em" is handled the exact
+% same way as input "D\dimexpr 1em\relax"; this is not
+% the case for the core units except for pt and pc (and sp), whose
+% conversion factors are the sole ones with a power of 2 denominator
+% (respectively 1, 1, and 65536). The further difference is that
+% for the core units apart from sp, the conversion factor is >1.
+%
+% We assume for this discussion T is non-negative.
+% If f/65536 > 1, the analysis is as above : some dimensions T sp
+% are not attainable as D uu, but the formula
+% N=round((2T+1)*32768/f)
+% will give a suitable decimal D via \the\dimexpr N sp\relax.
+% (if T=0, we get N=0 as 32768/f<0.5)
+% This D will let TeX convert D uu into T sp, if the dimension
+% is attainable else it will be a closest match
+% either from above or below (not necessarily nearest overall).
+%
+% If f/65536=1, attention that above formula would give N=1 for
+% T=0 (was bug #4).
+%
+% If f/65536<1, all dimensions Tsp are attainable as D uu. Indeed
+% D uu is parsed by TeX via N=round(D*65536), then T=trunc(N*phi),
+% with phi=f/65536. Starting from T we need to find an N such that
+% T/phi <= N< (T+1)/phi. We first consider v=(T+0.5)/phi. As its
+% distance to the extremities is 0.5/phi>0.5,(*) its rounding M
+% to an integer verifies automatically T/phi < M < (T+1)/phi, so
+% is a candidate. The TeX core conversion of Msp to a Dpt with
+% D a decimal of at most 5 fractional digits will provide a D
+% such that indeed M=round(D*65536).
+%
+% (*) attention that this fails if phi=1, we definitely can not
+% use the N=round((2T+1)*32768/f) formula for f=65536, it gives
+% N=T+1 in place of N=T... It is funny the formula works for all
+% f apart from f=65536...
+%
+% Attention that if T=0, the M=round(0.5/phi) will be at least 1,
+% so the produced decimal D will not be 0.0, although it will
+% be true that D \dimexpr f sp\relax produces the zero dimension,
+% the above analysis being correct even for T=0.
+%
% Implementation
% ==============
%
-% <comments to be added> (see discussion #2)
-% #2 is unit, assumed positive. We will need to branch whether
-% #2 is <1pt or >1pt.
+% \texdimenwithunit{dim1}{dim2}. dim2>0 assumed.
+% We first get f from dim2 and branch according to whether f>65536,
+% f=65536 or f<65536.
+% We will also need to check the sign of T (dim1=T sp).
+% We then compute in both branches round((2T+1)*32768/f), but
+% in an indirect way in the f<65536 branch to avoid overflow.
+% We will need also to intercept T=0 to avoid producing a non
+% pleasing positive D in that case (still correct for f<65536;
+% this is issue #3).
+%
+% In the f<65536 branch we first do the Euclidean division
+% 2T+1 = k*2*f + R with 0<=R<2f. The k is obtained as round((2T+1-f)/(2f))
+% which can be computed in a numexpr (and never gives -1 even for T=0)
+% Then (2T+1)*32768/f=65536*k + R*32768/f
+% Then R*32768/f<=65536-32768/f<65536-32768/65536=65536-0.5
+% Hence the numexpr evaluation gives an integer B<65536.
+%
+% N.B.: if f>=65536, we still have R*32768/f<65536 as R<2f
+% so the only difference is that the B could be here 65536
+%
+% From \the\dimexpr Bsp, we get a decimal E < 1, so E=0.ddddd
+% (or less digits) and the looked for D will be the contatenation
+% k.ddddd with k as obtained earlier. This procedure has no possible
+% arithmetic overflow.
+%
+% #2 is assumed positive. The case #2=1pt=65536sp is special, and
+% must be filtered out (issues #4, #6).
% pre-multiplication of #1 by 2 here
\def\texdimenwithunit#1#2{\expandafter\texdimenwithunit_
\the\numexpr\dimexpr#2\expandafter;\the\numexpr2*\dimexpr#1;}%
-\def\texdimenwithunit_#1;#2{\ifnum#1>65535
- \expandafter\texdimenwithunit_A\else\expandafter\texdimenwithunit_B\fi
- #2#1;%
+\let\texdimenorthat\texdimenfirstofone
+\def\texdimendothis#1#2\texdimenorthat#3{\fi#1}%
+\def\texdimenwithunit_#1;#2{%
+ \ifnum#1=\p@\texdimendothis\texdimenwithunit_p@\fi
+ \ifnum#1>\p@\texdimendothis\texdimenwithunit_A\fi
+ \texdimenorthat\texdimenwithunit_B#2#1;%
}%
-% unit>=1pt, handle this as for bp
+% we premultiplied the first argument by 2... must be undone now!
+% and we must avoid overflow. Very underoptimal, but user is not
+% supposed to do something as silly as actually using this unit=1pt case!
+\def\texdimenwithunit_p@#1#2;#3;{\expandafter\texdimenstrippt\the\dimexpr\numexpr#1#3/2sp\relax}%
+% unit>1pt, handle this as for bp. Attention it would be wrong for unit=1pt!
\def\texdimenwithunit_A#1#2;#3;{\expandafter\texdimenstrippt
\the\dimexpr\numexpr(#1#3+\if-#1-\fi1)*32768/#2sp\relax
}%
-% unit<1pt
-% if dim1<0, simply negate result for dim1>0 as it can not be 0.0
-\def\texdimenwithunit_B#1{\if-#1\expandafter\texdimenwithunit_Bneg\fi\texdimenwithunit_Ba#1}%
+% unit<1pt. Attention it would be wrong for unit=1pt!
+% if dim1<0, simply negate result for dim1>0 as it can not possibly be 0.0
+% Indeed (2T+1)*32768/f will be at least 3*32768/f so its rounding at least 2.
+% Add a check for dim1=0 to fix issue #3
+\def\texdimenzerominusfork #10-#2#3\krof {#2}%
+\def\texdimenwithunit_B#1{\texdimenzerominusfork
+ #1-\texdimenwithunit_Bzero
+ 0#1\texdimenwithunit_Bneg
+ 0-\texdimenwithunit_Ba
+ \krof#1}%
\def\texdimenwithunit_Ba#1#2;#3;{\expandafter\texdimenwithunit_Bb\the\numexpr#1#3+1;#2;}%
\def\texdimenwithunit_Bb#1;#2;{\expandafter\texdimenwithunit_Bc\the\numexpr(#1-#2)/(2*#2);#1;#2;}%
-% not adding f-expandability slight overhead here
-% oh well, let's do \the\numexpr..+0.ddddd which does the trick and allows
-% recycling strippt here with no need of another utility
-% (thinking about it it means we could do this for unit >1pt as this method
-% works even for \the\dimexpr producing 1pt or more... (as TeX outputs 1.0pt, not 1pt
-% so the dot remains to stop the \numexpr scan)
+% I was hesitating between leaving k in the stream (breaking f-expandability)
+% and then remove the "0" and trailing "pt" from 0.ddddd pt, but opted
+% finally for doing \the\numexpr..+0.ddddd which is f-expandable and allows
+% recycling strippt here with no need of another utility.
+%
+% This means (see the nota bene above) that we could apply this procedure
+% also for f>65536, because at worst we will get a \the\numexprk+1.0, which
+% gives the correct result. I tested and found about 39% longer execution time
+% if dim2>1pt does same calculations as for dim2<1pt, and at the same time
+% dim2<1pt was improved about 11% from skipping the conditional
\def\texdimenwithunit_Bc#1;#2;#3;{\the\numexpr#1+\expandafter\texdimenstrippt
\the\dimexpr\numexpr(#2-#1*2*#3)*32768/#3sp\relax}%
-% definitly not caring about f-expandability here
-\def\texdimenwithunit_Bneg\texdimenwithunit_Ba-#1;#2;%
- {-\expandafter\texdimenwithunit_Bb\the\numexpr#2+1;#1;}%
+% Here, definitely not caring about f-expandability. Or efficiency.
+\def\texdimenwithunit_Bneg-{-\texdimenwithunit_Ba{}}%
+\def\texdimenwithunit_Bzero#1;#2;{0.0}%
\texdimensendinput