1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
|
% 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/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 % 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
% \texdimenwithunit macro added at 0.99, refer to the
% comments of issue #2 on the tracker site.
%
% Is T sp attainable from unit "uu"?.
% If not, what is largest dimension < Tsp which is attainable?
% Here we suppose T>0.
%
% phi>1, psi=1/phi, psi<1.
%
% U(N,phi)=trunc(N phi) is the strictly increasing sequence,
% indexed by non-negative integers, of attainable dimensions.
% (in sp unit)
%
% U(N)<= T < U(N+1) iff N = ceil((T+1)psi) - 1
% U(M)< T <= U(M+1) iff M = ceil(T psi) - 1
%
% Stumbling block
% ---------------
%
% The stumbling block is that computing "ceil((T+1)psi) - 1" without
% overflow is not obvious: yes \numexpr/\dimexpr allow so-called
% "scaling operations" but only in the "rounding up" variant.
%
% If we attempt computing the ceil(x) function via round(x+0.5),
% for example with psi=100/7227 which corresponds to the unit "in",
% this necessitates evaluating:
%
% round((((T+1)*200)+7227)/14454)
%
% But as far as I can tell currently, for this we need to be able
% to evaluate without overflow (T+1)*200+7227 and this limits to
% T's which are (roughly) such that 100 T is less than \maxdimen.
%
% A work-around
% -------------
%
% The rest of the discussion is about an algorithm providing an
% alternative route to N, using \numexpr/\dimexpr/TeX facilities,
% and working with (almost, as we will see) the full range of allowed
% T's, 0 < T <= \maxdimen. (that the algorithm works for T=0 is to be
% checked manually after the main discussion).
%
% Let's return to the U(N)<= T < U(N+1) and U(M)< T <= U(M+1) equations.
%
% Either (recall in all of this T > 0):
%
% case1: M = N, i.e. T is not attainable, M=N < T psi < (T+1) psi <= N+1
% case2: M = N - 1, i.e. T is attained, T psi <= N < (T+1) psi, T = trunc(N phi)
%
% Let X = round(T psi). And let Y = trunc(X phi). We will explain later
% how X and Y can be computed using \numexpr/\dimexpr/TeX.
%
% case1: X can be N or N+1. It will be N+1 iff Y > T.
% case2: X can be N or N-1. It will be N iff trunc((X+1)phi)>T.
%
% This is not convenient: if Y < T it could be that we are in case 2
% but to decide we must check if trunc((X+1) phi) = T or not, so
% this means a second computation.
%
% If psi < 0.5
% ------------
%
% The situation then simplifies:
%
% case1: X can be N or N+1. It will be N+1 iff Y = trunc(X phi) > T.
% case2: X is necessarily N.
%
% Thus:
% a) compute X = round(T psi)
% b) compute Y = trunc(X phi) and test if Y > T. If true, we
% were in case 1, replace X by X - 1, else we were either
% in case 1 or case 2, and we leave X as it is.
% We have thus found N.
%
% The operation Y = trunc(X phi) can be achieved this way:
% i) use \the\dimexpr to convert X sp into D pt,
% ii) use \the\numexpr\dimexpr to convert "D uu" into sp.
% These steps give Y.
%
% This way we find the maximal dimension at most T sp exactly
% representable in "uu" unit.
%
% The computations of X and Y can be done independently of sign of T.
% But the final test has to be changed to Y < T if T < 0 and then
% one must replace X by X+1. So we must filter out the sign of the input.
%
% If the goal is only to find a decimal D such that "D uu" is
% exactly T sp in the case this is possible, then things are simpler
% because from X = round(T psi) we get D such as X sp is same as D pt
% and "D uu" will work.
% We don't have to take sign into account for this computation.
% But if T sp was not attainable we don't know if this X will give
% a D such that D uu < T sp or D uu > T sp.
%
% If psi > 0.5
% ------------
%
% For example unit "bp" has phi=803/800.
%
% It is then not true that if T sp is attainable, the X = round(T psi)
% will always work.
%
% But it is true that R = round((T + 0.5) psi) will always work.
% Here we must use -0.5 if T < 0, though.
%
% This R=round((T+0.5) psi) can always be computed via \numexpr because 2T+1
% will not trigger arithmetic overflow.
%
% So this gives an approach to find a D such that "D uu" is exactly
% T sp when this is possible.
%
% If Tsp (positive) is not attainable, this R however can produce
% either N or N+1.
%
% But we can decide what happened by computing Z = trunc(R phi).
% If and only if Z > T this means R was N+1.
%
% It is slightly less costly to compute X = round(T psi) than
% R = round((T + 0.5) psi),
% but if we then realize that trunc(X phi) < T we do not yet know
% if trunc((X+1) phi) = T or is > T. So we proceed via R, not X,
% to not have to make a second computation if a dimension comparison
% test goes awry.
%
% To recapitulate: we have our algorithm for all units to find out
% maximal dimension exactly attainable in "uu" unit and at most equal
% to (positive) T sp.
%
% Unfortunately the check that Y (in case psi < 0.5) or Z (in case psi >
% 0.5) verifies or not Y > T may trigger a Dimension too large error if
% T sp was near non-attainable \maxdimen. It turns out this sad
% situation happens only for the units `dd`, `nc`, and `in`, and T sp
% very close to \maxdimen (like for all units apart from `pt`, `bp`,
% `nd`, the \maxdimen is not attainable, and by bad luck for `dd`, `nc`,
% and `in`, the X will correspond to a decimal D such that Duu>\maxdimen
% is the nearest virtually attaible dimensions from above not from
% below; see the README.md for the tabulation of the maximal usable inputs).
%
% Regarding the \texdimen<uu> macros, and units with phi > 2, I
% hesitated using either the round((T+0.5)psi) or round(T psi), but for
% Tsp = \maxdimen, both formulas turned out to give the same result for
% all such units, so I chose for these \texdimen<uu> macros and the
% units with phi>2 to use the simpler round(T psi) which does not need
% to check the sign of T.
%
% For the "up" and "down" macros, we again use the round(T psi), but do
% have to check the sign anyhow. We could also have used the
% round((T+0.5)psi) which requires a sign check too, but it costs a bit
% more. It would have allowed though to share the same codebase for all
% units, here we have to prepare some slightly different shared macros
% for the first batch bp, nd, dd and the second batch mm, pc, nc, cc,
% cm, in.
%
% Implementation
% ==============
%
\def\texdimenfirstofone#1{#1}%
{\catcode`p 12\catcode`t 12
\csname expandafter\endcsname\gdef\csname texdimenstrippt\endcsname#1pt{#1}}%
%
% down macros:
% for units with phi < 2:
\def\texdimendown_A#1{\if-#1\texdimendown_neg\fi\texdimendown_B#1}%
\def\texdimendown_B#1;#2;{\expandafter\texdimendown_c\the\numexpr(2*#1+1)#2;#1;}%
% for units with phi > 2:
\def\texdimendown_a#1{\if-#1\texdimendown_neg\fi\texdimendown_b#1}%
\def\texdimendown_b#1;#2;{\expandafter\texdimendown_c\the\numexpr#1#2;#1;}%
% shared macros:
\def\texdimendown_c#1;{\expandafter\texdimendown_d\the\dimexpr#1sp;#1;}%
{\catcode`P 12\catcode`T 12\lowercase{\gdef\texdimendown_d#1PT};#2;#3;#4;%
{\ifdim#1#4>#3sp \texdimendown_e{#2}\fi\texdimenfirstofone{#1}}%
}%
% this #2 will be \fi
\def\texdimendown_e#1#2#3#4{#2\expandafter\texdimenstrippt\the\dimexpr\numexpr#1-1sp\relax}%
% negative branch:
% The problem here is that if input very small, output can be 0.0, and we
% do not want -0.0 as output.
% So let's do this somewhat brutally and non-efficiently.
% Anyhow, negative inputs are not our priority.
% #1 is \fi here and #2 is \texdimendown_b or _B:
\def\texdimendown_neg#1#2-#3;#4;#5;{#1\expandafter\texdimenstrippt\the\dimexpr-#2#3;#4;#5;pt\relax}%
%
% up macros:
\def\texdimenup_A#1{\if-#1\texdimenup_neg\fi\texdimenup_B#1}%
\def\texdimenup_B#1;#2;{\expandafter\texdimenup_c\the\numexpr(2*#1+1)#2;#1;}%
\def\texdimenup_a#1{\if-#1\texdimenup_neg\fi\texdimenup_b#1}%
\def\texdimenup_b#1;#2;{\expandafter\texdimenup_c\the\numexpr#1#2;#1;}%
\def\texdimenup_c#1;{\expandafter\texdimenup_d\the\dimexpr#1sp;#1;}%
{\catcode`P 12\catcode`T 12\lowercase{\gdef\texdimenup_d#1PT};#2;#3;#4;%
{\ifdim#1#4<#3sp \texdimenup_e{#2}\fi\texdimenfirstofone{#1}}%
}%
% this #2 will be \fi
\def\texdimenup_e#1#2#3#4{#2\expandafter\texdimenstrippt\the\dimexpr\numexpr#1+1sp\relax}%
% negative branch:
% Here we can me more expeditive than for the "down" macros.
% But this breaks f-expandability.
% #1 will be \fi and #2 is \texdimenup_b or _B:
\def\texdimenup_neg#1#2-{#1-#2}%
%
% pt
%
\def\texdimenpt#1{\expandafter\texdimenstrippt\the\dimexpr#1\relax}%
%
% bp 7227/7200 = 803/800
%
\def\texdimenbp#1{\expandafter\texdimenbp_\the\numexpr\dimexpr#1;}%
\def\texdimenbp_#1#2;{%
\expandafter\texdimenstrippt\the\dimexpr\numexpr(2*#1#2+\if-#1-\fi1)*400/803sp\relax
}%
% \texdimenbpdown: maximal dim exactly expressible in bp and at most equal to input
\def\texdimenbpdown#1{\expandafter\texdimendown_A\the\numexpr\dimexpr#1;*400/803;bp;}%
% \texdimenbpup: minimal dim exactly expressible in bp and at least equal to input
\def\texdimenbpup#1{\expandafter\texdimenup_A\the\numexpr\dimexpr#1;*400/803;bp;}%
%
% nd 685/642
%
\def\texdimennd#1{\expandafter\texdimennd_\the\numexpr\dimexpr#1;}%
\def\texdimennd_#1#2;{%
\expandafter\texdimenstrippt\the\dimexpr\numexpr(2*#1#2+\if-#1-\fi1)*321/685sp\relax
}%
% \texdimennddown: maximal dim exactly expressible in nd and at most equal to input
\def\texdimennddown#1{\expandafter\texdimendown_A\the\numexpr\dimexpr#1;*321/685;nd;}%
% \texdimenndup: minimal dim exactly expressible in nd and at least equal to input
\def\texdimenndup#1{\expandafter\texdimenup_A\the\numexpr\dimexpr#1;*321/685;nd;}%
%
% dd 1238/1157
%
\def\texdimendd#1{\expandafter\texdimendd_\the\numexpr\dimexpr#1;}%
\def\texdimendd_#1#2;{%
\expandafter\texdimenstrippt\the\dimexpr\numexpr(2*#1#2+\if-#1-\fi1)*1157/2476sp\relax
}%
% \texdimendddown: maximal dim exactly expressible in dd and at most equal to input
\def\texdimendddown#1{\expandafter\texdimendown_A\the\numexpr\dimexpr#1;*1157/2476;dd;}%
% \texdimenddup: minimal dim exactly expressible in dd and at least equal to input
\def\texdimenddup#1{\expandafter\texdimenup_A\the\numexpr\dimexpr#1;*1157/2476;dd;}%
%
% mm 7227/2540 phi now >2, use from here on the simpler approach
%
\def\texdimenmm#1{\expandafter\texdimenstrippt\the\dimexpr(#1)*2540/7227\relax}%
% \texdimenmmdown: maximal dim exactly expressible in mm and at most equal to input
\def\texdimenmmdown#1{\expandafter\texdimendown_a\the\numexpr\dimexpr#1;*2540/7227;mm;}%
% \texdimenmmup: minimal dim exactly expressible in mm and at least equal to input
\def\texdimenmmup#1{\expandafter\texdimenup_a\the\numexpr\dimexpr#1;*2540/7227;mm;}%
%
% pc 12/1
%
\def\texdimenpc#1{\expandafter\texdimenstrippt\the\dimexpr(#1)/12\relax}%
% \texdimenpcdown: maximal dim exactly expressible in pc and at most equal to input
\def\texdimenpcdown#1{\expandafter\texdimendown_a\the\numexpr\dimexpr#1;/12;pc;}%
% \texdimenpcup: minimal dim exactly expressible in pc and at least equal to input
\def\texdimenpcup#1{\expandafter\texdimenup_a\the\numexpr\dimexpr#1;/12;pc;}%
%
% nc 1370/107
%
\def\texdimennc#1{\expandafter\texdimenstrippt\the\dimexpr(#1)*107/1370\relax}%
% \texdimenncdown: maximal dim exactly expressible in nc and at most equal to input
\def\texdimenncdown#1{\expandafter\texdimendown_a\the\numexpr\dimexpr#1;*107/1370;nc;}%
% \texdimenncup: minimal dim exactly expressible in nc and at least equal to input
\def\texdimenncup#1{\expandafter\texdimenup_a\the\numexpr\dimexpr#1;*107/1370;nc;}%
%
% cc 14856/1157
%
\def\texdimencc#1{\expandafter\texdimenstrippt\the\dimexpr(#1)*1157/14856\relax}%
% \texdimenccdown: maximal dim exactly expressible in cc and at most equal to input
\def\texdimenccdown#1{\expandafter\texdimendown_a\the\numexpr\dimexpr#1;*1157/14856;cc;}%
% \texdimenccup: minimal dim exactly expressible in cc and at least equal to input
\def\texdimenccup#1{\expandafter\texdimenup_a\the\numexpr\dimexpr#1;*1157/14856;cc;}%
%
% cm 7227/254
%
\def\texdimencm#1{\expandafter\texdimenstrippt\the\dimexpr(#1)*254/7227\relax}%
% \texdimencmdown: maximal dim exactly expressible in cm and at most equal to input
\def\texdimencmdown#1{\expandafter\texdimendown_a\the\numexpr\dimexpr#1;*254/7227;cm;}%
% \texdimencmup: minimal dim exactly expressible in cm and at least equal to input
\def\texdimencmup#1{\expandafter\texdimenup_a\the\numexpr\dimexpr#1;*254/7227;cm;}%
%
% in 7227/100
%
\def\texdimenin#1{\expandafter\texdimenstrippt\the\dimexpr(#1)*100/7227\relax}%
% \texdimenindown: maximal dim exactly expressible in in and at most equal to input
\def\texdimenindown#1{\expandafter\texdimendown_a\the\numexpr\dimexpr#1;*100/7227;in;}%
% \texdimeninup: minimal dim exactly expressible in in and at least equal to input
\def\texdimeninup#1{\expandafter\texdimenup_a\the\numexpr\dimexpr#1;*100/7227;in;}%
% both in and cm
% Mathematics ("both" macros)
% ===========
%
% Let a and b be two non-negative integers such that U = floor(a 7227/100) =
% floor(b 7227/254). It can be proven that a=50k, b=127k for some integer k.
% The proof is left to reader. So U = floor(7227 k /2) for some k.
%
% Let's now find the largest such U <= T. So U = floor(k 7227/2)<= T which is
% equivalent (as k is integer) to k 7227/2 <= T + 1/2, i.e.
%
% kmax = floor((2T+1)/7227)
%
% If we used for x>0 the formula floor(x)=round(x-1/2)=<x-1/2> we would end
% up basically with some 4T hence overflow problems even in \numexpr.
% Here I used <.> to denote rounding in the sense of \numexpr. It is not
% 1-periodical due to how negative inputs are handled, but here x-1/2>-1/2.
%
% The following lemma holds: let T be a non-negative integer then
%
% floor((2T+1)/7227) = <(2T - 3612)/7227>
%
% So we can compute this k, hence get a=50k, b=127k, all within \numexpr and
% avoiding overflow.
%
% Implementation
% ==============
%
% Regarding the output in pt or sp, we seem to need floor(k 7227/2).
% The computation of floor(k 7227/2) as <(7227 k - 1)/2> would require to
% check if k==0 so we do it rather as <(7227 k + 1)/2> - 1. No overflow
% can arise as k = 297147 for \maxdimen, and then 7227 k = 2**31 - 2279 and
% there is ample room for 7227k+1 using \numexpr.
%
% But this step, as well as initial step to get kmax will require to separate
% handling of negative input from positive one.
%
% Alternative
% -----------
%
% For non-negative T we can compute U = ((T+1)/7227)*7227. If U <= T keep it,
% else if U > T, replace it by U - 3614. This is alternative road to the maximal
% floor(k 7227/2) at most equal to T.
%
% There is some slight under-efficiency to share macros across the 3 end targets
% as I added one layer of parentheses.
\def\texdimenbothincm#1{\expandafter\texdimenstrippt\the\dimexpr
\expandafter\texdimenboth_a\the\numexpr\dimexpr#1;127);}%
\def\texdimenbothcmin#1{\expandafter\texdimenstrippt\the\dimexpr
\expandafter\texdimenboth_a\the\numexpr\dimexpr#1;50);}%
\def\texdimenbothincmpt#1{\expandafter\texdimenstrippt\the\dimexpr
\expandafter\texdimenboth_a\the\numexpr\dimexpr#1;7227+1)/2-1;}%
\let\texdimenbothcminpt\texdimenbothincmpt
\def\texdimenboth_a#1{\if-#1\texdimenboth_neg\fi\texdimenboth_b#1}%
% The opening parenthesis ( is closed in #2, it was added to share "pt" output
% with the two others
\def\texdimenboth_b#1;#2;{\numexpr(((2*#1-3612)/7227)*#2sp\relax}%
% negative branch. This is expanded in a \dimexpr so we can insert the -
% in front of the \numexpr.
% #1 is \fi here and #2 is \texdimenboth_b
\def\texdimenboth_neg#1#2-#3;#4;{#1-\numexpr(((2*#3-3612)/7227)*#4sp\relax}%
%
% \texdimenbothincmsp is done separately as I found no easy way to share
% its macros with the others; alternative would have been to make it the
% core, and derive the others from it, (\texdimencm{\texdimenbothincmsp{...}sp})
% but then they would be less efficient than their current versions.
% (it is a bit ironical to worry about not creating too many macros
% in such a small package, by the way)
\def\texdimenbothincmsp#1{\the\numexpr\expandafter\texdimenbothsp_a\the\numexpr\dimexpr#1;}%
\def\texdimenbothsp_a#1{\if-#1\texdimenbothsp_neg\fi\texdimenbothsp_b#1}%
\def\texdimenbothsp_b#1;{(((2*#1-3612)/7227)*7227+1)/2-1\relax}%
% #1 is \fi
% we need to regrab here or to add a \numexpr..\relax layer to
% \texdimenbothsp_b (parentheses could do but using 0-(...) syntax)
% finally doing the job of \texdimenbothsp_b directly
\def\texdimenbothsp_neg#1#2-#3;{#1-\numexpr(((2*#3-3612)/7227)*7227+1)/2-1\relax\relax}%
%
\let\texdimenbothcminsp\texdimenbothincmsp
% (\texdimenwithunit, added at 0.99)
% Mathematics
% ===========
%
% 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
% ==============
%
% \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;}%
\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;%
}%
% 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. 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;}%
% 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}%
% Here, definitely not caring about f-expandability. Or efficiency.
\def\texdimenwithunit_Bneg-{-\texdimenwithunit_Ba{}}%
\def\texdimenwithunit_Bzero#1;#2;{0.0}%
\texdimensendinput
|