LaTeX 数学模式的所有间距,都出自三个数字。它们就写在 fontmath.ltx 的最后三行:\thinmuskip=3mu、\medmuskip=4mu plus 2mu minus 4mu、\thickmuskip=5mu plus 5mu。细空、中空、粗空——3、4、5——既决定了 a+b 中 + 周围的空隙,也决定了 a=b 中 = 周围的空隙。而且中空可以收缩整整 4 mu,也就是说它 可能完全消失,肯为此写上一笔的参考书并不多。本页梳理上标 ^ 与下标 _ 的规则,并列出 \,、\:、\;、\!、\quad、\qquad 的实测宽度。无论你是为「latex 空白 调整」而来,还是被 ! Double superscript 逼来的,答案都在这里。
^ 与 _ 只作用于紧随其后的一个 token
在数学模式中,^ 生成上标、_ 生成下标,但作用范围 只有紧随其后的一个 token——一个字符,或一条命令。要抬起或下沉多于一个字符,就用花括号 { } 括成一体。麻烦之处在于:忘了也不会报错。x^{10} 会把整个「10」变成指数,而少了花括号的 x^10 只把「1」抬上去,「0」仍以正常大小留在基线上。它会静悄悄地编译通过,于是往往到印出来才发现。请让手养成习惯:超过一个字符的上下标一律加花括号。
$x^{10}$ % the whole 10 is the exponent
$x^10$ % only the 1 is raised; the 0 stays full size
$a_{ij}$ % ij together as one subscript
$2^{n+1}$ % n+1 as one exponent
$x_i^2$ % both scripts; the order does not matter同一个符号可以 同时带上标和下标。写成 x_i^2 或 x^2_i 都行,LaTeX 会以相同方式堆叠。上下标也能嵌套,但每一层都要各自的花括号:x^{y^z} 中,z 是 y 的指数,而整个 y^z 是 x 的指数。去掉花括号,x^y^z 就会触发下一节的错误。另外,在 \sum、\lim 这类大型运算符上,^ 与 _ 可能移到符号的 正上方与正下方,而 \limits/\nolimits 可以强制其位置;这一行为属于运算符那边的话题,交由求和、积分与大型运算符那一页负责。
! Double superscript. 与 ! Double subscript.
这是给同一个底座 两次 加上标时出现的错误。编译 $x^y^z$,TeX Live 2024 的 pdfLaTeX 会以 ! Double superscript. 停下;下标方面,$x_i_j$ 会给出 ! Double subscript.。两者的意思都是「这个底座已经有一个上下标了」。改法是 把你真正想要的嵌套用花括号写出来:x^{y^z} 表示 z 是 y 的指数,而 {x^y}^z 表示把整个 x^y 取 z 次幂。这两者在数学上并不相同,所以若只为消掉错误而随手放花括号,式子的含义就变了。
导数的撇号 f'(x)——为何不能写 f^'
只需打一个撇号 '。它 本身就已经是上标,所以千万别再自己加 ^。latex.ltx 把数学模式中的 ' 设为活性字符并展开为 ^\bgroup\prim@s;实测中,f'(x) 与 f^{\prime}(x) 都是 22.27089 pt,完全一致。二阶写 f''(x),三阶写 f'''(x)。有趣的是,这套机制会 吞掉紧随其后的 ^:f'^2 不会报错,排出来是 12.76395 pt,与 f^{\prime2} 完全相同。所以若你想表达「先撇号再平方」,请先确认结果是否合乎本意。
把上下标放到左侧:{}^{A}_{Z}X 与 \sideset
若要把上标或下标放到符号的 左侧——比如同位素记法——就 以一对空花括号 {} 作底座。写成 {}^{14}_{6}\mathrm{C},{} 会作为「零宽的普通符号」放下,上下标挂在它上面,随后才排 C。若省掉这个 {},上下标就会 擅自挂到前面的东西上:写成 = ^{14}_{6}C,它们就吊在 = 上;如果前面本来就带上下标,还会得到 ! Double superscript.。要在大型运算符两侧分别放上下标,可用 amsmath 的 \sideset{_a^b}{_c^d}\sum——但那是运算符那边的工具,详见求和与积分那一页。mathtools 的 \prescript 是同一需求的另一种解法。
\[
{}^{14}_{6}\mathrm{C}, \qquad {}^{t}\!A, \qquad
x^{y^z} \ne {x^y}^z, \qquad f''(x)
\]mu 这个单位:1 mu 等于 1/18 em,而且在上下标中会缩小
1 mu 等于 1/18 em,也就是 18 mu 恰好是 1 em。这里的 em 与数学符号字体的大小挂钩,所以用 mu 写出的空白 会随字号变化。在 10 pt 的 article 中测量 a\,b 与 ab 的宽度差,为 1.66662 pt——与 3 mu = 1/6 em = 1.6667 pt 吻合。把同一个 \, 放进上标,如 x^{a\,b},则量得 1.36574 pt;再深一层的上下标里缩到 1.22685 pt。换到 12 pt 文档则变为 1.99997 pt。这正是空白随字号伸缩的证据。
不过 \quad 与 \qquad 不属于这一族。latex.ltx 的定义是 \def\quad{\hskip1em\relax},\qquad 为 2 em——单位不是 mu,而是 正文字体的 em。因此它们放进上下标里也不会缩小:实测 x^{a\quad b} 比素的 x^{ab} 宽 10.00002 pt,正是一个正文大小的 em。而且在 12 pt 文档中,\quad 量得 11.74988 pt,而 18 mu 恰是 12 pt——也就是说 \quad 与 18 mu 并不等长。稳妥的分工是按职责来:细调用 mu 系(\,、\:、\;、\!),大分隔用 em 系(\quad、\qquad)。
| 命令 | 定义的量 | 10pt article 实测 |
|---|---|---|
\, | \thinmuskip = 3 mu(别名 \thinspace) | 1.66662 pt;最常用的微调 |
\: | \medmuskip = 4 mu +2 −4(\medspace、\>) | 2.22216 pt;有弹性,可能被压没 |
\; | \thickmuskip = 5 mu +5(\thickspace) | 2.77771 pt;拉伸时可达两倍 |
\! | −3 mu(与 \negthinspace 相同) | −1.66663 pt;正是 \, 的反向 |
\negmedspace | −4 mu;无需 amsmath(就在 latex.ltx 里) | −2.22217 pt |
\negthickspace | −5 mu;同样在 LaTeX 内核中 | −2.77771 pt |
\quad | \hskip1em;是正文的 em,不是 mu | 10.00002 pt;在上下标中也不缩小 |
\qquad | \hskip2em;\quad 的两倍 | 20.00003 pt |
(backslash-space) | 控制空格;与正文词间距相同 | 3.33332 pt |
\enspace | 固定的半个 em | 5 pt |
\, 之所以在正文和数学模式中都能用,是因为 latex.ltx 中的 \tmspace 按 \ifmmode 分支:数学模式发出 \mskip\thinmuskip(3 mu),正文则是固定的 \kern .16667em。同一条命令,两套单位。 顺带一提,latex.ltx 里还写着 \let\>=\:,说明 \> 是 \: 的别名。另外,表中的 \negmedspace 与 \negthickspace 常被说成来自 amsmath,但在 TeX Live 2024 的 LaTeX 内核中二者都已存在,经确认无需 \usepackage{amsmath} 也能用。
下标为什么会变小——\displaystyle 与四种数学样式
TeX 会把每个公式放进四种样式之一,样式同时决定字符大小和一个 mu 的实际尺寸。 由大到小依次是 \displaystyle(行间公式)、\textstyle(行内)、\scriptstyle(上下标)、\scriptscriptstyle(上下标的上下标)。在 10pt 正文里量 X 的高度:display 和 text 都是 6.83331pt,完全一样;script 是 4.78334pt,scriptscript 是 3.41667pt。变小只发生在进入上下标的这两步,而且到此为止,没有更小的第五种样式。上一节说 mu 会缩,指的就是这套机制。日常真正有用的是强制指定:行内的 $\sum_{k=1}^{n}$ 被压成 8.04175pt 高,写成 $\displaystyle\sum_{k=1}^{n}$ 则是 16.51393pt——回到行间公式的样子,求和的上下限也重新回到上下方。代价是行距会变得不齐,所以如果在正文里反复要用它,多半说明这个公式本该另起一行。
实务中最常用的三处:dx 前的 \,、\int\!\!\int、以及函数名之后
先说积分。\int f(x)dx 量得 38.71877 pt,\int f(x)\,dx 为 40.38539 pt——相差正好是 3 mu 的 1.66662 pt,而这个细空正是把被积函数和 dx 分开的东西。不加的话,f(x)dx 看上去就是一串连在一起的符号。再说重积分。\int\!\!\int 是用两个负空把积分号拉近的经典写法,实测 它与 amsmath 的 \iint 宽度完全一致(都是 19.3056 pt;素的 \int\int 为 22.63885 pt)。也就是说,\iint 就是把这套手工调整正式做成了一条命令。
\[
\int f(x)\,dx, \qquad \int\!\!\int f \;(=\ \iint f),
\qquad \sin x, \qquad \operatorname{sinc} x
\]第三处是函数名之后的空隙,而这里 你不必自己动手。\sin 被声明为算子(Op),所以它与后续符号之间会自动出现一个细空。实测 \sin x 为 19.6597 pt,而 \mathrm{sin}x 为 17.99307 pt——相差 1.66663 pt,恰好是 3 mu。所以用 \mathrm{} 写自造的函数名,丢掉的正是这个空隙。正确的做法是用 amsmath 的 \operatorname{sinc};实测 \operatorname{sin}x 也是 19.6597 pt,与 \sin x 相同。若该名字反复出现,就用 \DeclareMathOperator 定义一次。
用不可见的盒子对齐高度与宽度:\phantom、\vphantom、\mathstrut
「只占位置,不显示内容」——排版中反复出现的这一需求,正是 \phantom 一族的职责。\phantom{…} 会造出一个与其参数按正常方式排出时 等高、等深、等宽 的空盒。什么也不印,但那么大的空间确实被留了出来。还有只保留某一维度的版本:\hphantom{…} 只保留宽度(高与深为零),\vphantom{…} 只保留高度与深度(宽为零)。当你「想主张竖直方向的空间,但不希望横向占地」时,就该用 \vphantom。
\[
\sum_{j \in \{0,\ldots,10\} \vphantom{3^{3^{3^j}}}}
\sum_{i \in \{0,\ldots,3^{3^{3^j}}\}} i \cdot j
\]
% level the radicals: each argument gets a parenthesis of vertical room
$\sqrt{\mathstrut a}\;\sqrt{\mathstrut a^2}\;\sqrt{\mathstrut b}$上面的例子正是两个求和号对不齐的场景。右侧的求和在下标里带着 3^{3^{3^j}} 这座高塔,TeX 便把该求和的主体往下压以腾出空间,于是左右两个 \sum 就错位了。在左侧放上 \vphantom{3^{3^{3^j}}},就能 在不显形的前提下占住同样的高度,两者随即对齐。近亲 \mathstrut 是不接受参数的专用支柱,其内部就是 \vphantom(——也就是 与开括号 ( 等高等深、宽度为零。当 \sqrt{a} 与 \sqrt{a^2} 并排时,在每个参数开头放一个,根号的顶盖就齐了,整行也就安稳了。一句话:要对齐宽度用 \hphantom,要对齐高度用 \vphantom 或 \mathstrut,两者都要就用 \phantom——它们都建立在同一个想法之上:借助不可见的盒子,只操纵尺寸。