该函数round_xn_over_d
以缩放后的点值为参数,返回缩放后的点值,其含义如下:
假设我们有\mydimen=10truept
。众所周知,如果\magnification
使用,则“真实”尺寸不会被放大(参见第 60 页电子书)。round_xn_over_d
在 TeX 的内部表示中使用会得到与未使用时\mydimen
相同的值。\mydimen
\magnification
这是函数体的简化版本(x
-参数-应该是正数)round_xn_over_d
(写成C语言):
int n = mag;
int d = 1000;
unsigned int t, u, v;
t = (x % 0100000) * n;
u = (x / 0100000) * n + (t / 0100000);
v = (u % d) * 0100000 + (t % 0100000);
/* it is supposed that u/d < 0100000 */
u = 0100000 * (u/d) + (v/d);
v = v % d;
if (2*v >= d)
incr(u);
return u;
它是如何工作的?
答案1
(冗长的回答,一边打字一边思考。稍后会尝试写一个较短的版本。)
0100000
表示八进制100000
,即 8 5 = 2 15 = 32768。因此,让我们看一下问题中的代码片段,用0100000
替换,32768
因为我们今天对八进制都不太熟悉。我们可以省略n
和的具体值d
:它们并不重要,除非我们假设0 <= n < 32768
和0 <= d
:
unsigned int t, u, v;
t = (x % 32768) * n;
u = (x / 32768) * n + (t / 32768);
v = (u % d) * 32768 + (t % 32768);
/* it is supposed that u/d < 32768 */
u = 32768 * (u/d) + (v/d);
v = v % d;
if (2*v >= d)
incr(u);
return u;
我们将整数写x
为a*32768 + b
其中a = x/32768
和b = x % 32768
。然后代码片段变成:
unsigned int t, u, v;
t = b * n;
u = a * n + (t / 32768);
v = (u % d) * 32768 + (t % 32768);
/* it is supposed that u/d < 32768 */
u = 32768 * (u/d) + (v/d);
v = v % d;
if (2*v >= d)
incr(u);
return u;
为了进一步理解这一点,请考虑以下几点:
32768s place units place
x a b
x*n a*n b*n (multiply both components by n)
x*n a*n t (definition of t)
x*n a*n + t/32768 t%32768 (normalize, i.e. "carry" to the 32768s place)
x*n u t%32768 (definition of u)
到目前为止,我们已经乘以了x
。n
接下来我们要除以d
。我们可以像通常做长除法那样做:
32768s place units place
x*n u t%32768 (from earlier)
→ divide the first component by d, and shift the remainder:
u/d (u%d)*32768 + (t%32768)
u/d v (definition of v)
→ also divide the unit's component by d:
u/d v/d, with remainder v%d
将此结果读为数字可得出商。为了避免因重复使用变量而产生的混淆,让我们调整代码行,将商和余数称为uu
and ,vv
而不是u
and v
:
/* it is supposed that u/d < 32768 */
uu = 32768 * (u/d) + (v/d);
vv = v % d;
最后是四舍五入:uu
上面是商(除法的整数部分),但是如果余数(vv
)恰好大于的一半d
,那么就意味着我们应该向上舍入:
if (2*vv >= d)
incr(uu);
我们现在可以返回结果:
return uu;
我们从来不用使用x
实际上恰好是缩放值的事实;即使将x
视为整数,该函数也能正常工作。此代码的目的只是计算x * n / d
正确舍入的值,而不会导致任何结果超过 32768 × 32768 = 2 30。
换句话说:假设我们给出了三个整数x
,n
和d
,它们都保证小于 2 30(并且n
小于 32768),并且我们想要计算(半整数向上舍入)的值x * n / d
,而不超过值2^30
。这就是我们要做的。