LaTeX 公式排版注意事项/常见错误

参考上海交大 BBS 的帖子 “【连载】奇迹与魔法,可不是免费的哦……——(La)TeX 公式不为人知的一面(2020/㋋㏲更新)”

参考 强迫症的 LaTeX 学习笔记

LaTeX 是世上最丑陋的语言,但又能生成最漂亮的文档。 —— 谢益辉 《是莽撞人就来单挑:还世界一个轻量级 TeXLive》

见过无数人打括号不加\left\right,讲讲 (La)TeX 公式不为人知的一面。下面的很多内容基于 The TeXbook 和各种 LaTeX 宏包使用说明文档编写。

括号的大小

那么就从无数人出问题的地方开始。该加的时候\left\right是必须要加的

例:上海交通大学高等数学课程网站 → 课程辅导 → 第十章 线面积分 → 格林定理及其应用 |219x53

这里围道积分符号也没有正确显示,老字体毛病了。

注:此例中的公式并非用 TeX 排版,只是为了说明而用。全文同。

加了\left\right后,括号就伸展得差不多了。

\iint\limits_D\left(…\right)…

\[\iint\limits_{D}\left(\frac{\partial Q}{\partial x}-\frac{\partial P}{\partial y}\right)dx\,dy \]

手工调节括号的大小

有的时候\left\right也不够玩的,这时需要手工调节括号的大小:

  • \(\bigl((x+1)(x-1)\bigr)^2\) 这种括号套括号的情况,用\bigl\bigr(用法和\left\right差不多)手工指定括号的大小为比普通的括号大一点点。
    • 如果套了第三层括号的话……禁止套娃!
  • 求和符号带上下限的时候,需要手工调节括号的大小,因为自动生成的括号太大了。括号不是非要完全牢牢包裹住里面的东西的。

\biggl(\sum_{i=1}^n a_i \biggr)^2

\[\biggl(\sum_{i=1}^n a_i \biggr)^2 \]

对于 TeX 默认使用的 Computer Modern 字体,使用的命令与括号的大小之间有很明显的联系,这样就容易估算需要的括号大小了。

左括号前 右括号前 尺寸
\left \right 自动匹配
不加 不加 单行
\bigl \bigr 比单行大一点,用于括号套括号
\Bigl \Bigr 1.5 行
\biggl \biggr 2 行,用于分数、求和、积分等
\Biggl \Biggr 2.5 行

\left\right带来的间距空格问题

使用\left\right后,即使括号大小没有变化,显示效果也会和不使用时不一样:

\(2(a+b)\) 2(a+b)
\(2\left(a+b\right)\) 2\left(a+b\right)

这里加空格是不对的。此功能可能是 TeX 作者有意为之,但更疑似 TeX 的设计缺陷。我在文档中重新定义了\left\right来消除这些间距;对于水源所用的 MathJax 公式,可以用\!抵消这一间距。

\(2(a+b)\) 2(a+b)
\(2\!\left(a+b\right)\) 2\!\left(a+b\right)

消除\left\right引入的额外间距的方法:

\let\originalleft\left
\let\originalright\right
\renewcommand\left{\mathopen{}\mathclose\bgroup\originalleft}
\renewcommand\right{\aftergroup\egroup\originalright}

来自 Spacing around \left and \right

函数的字体和间距空格

log、sin、arctan、Re 等函数需要用正体并在前后加上空格。若两者都不做,就搞不清 atanx 究竟是 arctan x 的缩写还是变量 a 乘以 tan x

例:上海交通大学数学系组编《数学物理方法(第二版)》p. 80 被积函数是变量 R、自然对数的底 e、积分变量 z 的乘积吗?

所幸在 TeX 中,许多公认的函数都被预先定义过了,只需要在函数名前加\便可自动达成排版效果。

但是,一些在特定领域中重要的函数没有被预先定义,例如伴随矩阵 adj、对角矩阵 diag;\Re\Im 则被字母 R 和 I 的一种花式写法取代。自己设的函数当然也不可能被预先定义。使用\operatorname{…}来自定义:

\operatorname{adj} A \(\operatorname{adj}A\)

k \operatorname{Re} z^2 \(k\operatorname{Re}z^2\)

\DeclareMathOperator是在 LaTeX 中定义这类函数的惯用方法,但是它把知乎的公式编辑器整挂了,感觉水源的公式也会被搞,这里就不提了……

微分符号 d 的间距

口算: \(\int sinxdx=\mathord{?}\)

如果你的回答是 \(-\cos x\) ,恭喜!你不仅会被 +C 警察出警,让你高数 I 重修,你还没有发现 cos 函数既不是正体又没加空格,本帖上一节内容白读了。

当然,这不是重点。为了明确 xdy 指变量 x、变量 d、变量 y 的乘积,还是指 x 乘以微分 dy,对微分符号 d 也有与函数类似的规定:d 之前加空格

这个空格在 TeX 中写作\,,因此 x dy 应写为x\,dy

排版优秀的书籍,在间距上不会妥协。

例:上海交通大学数学科学学院微积分课程组编《大学数学·微积分(第二版)(下册)》p. 277 sin 和 cos 之前没有足够的间距,x 和 dx 之间也缺乏间距。(好在这些都不影响理解。) 逗号之后也缺乏应有的间距,使“0, 1, 2”之间的逗号与“n =”之前的逗号看上去是同一层次的。

例:Michael Spivak, Calculus (1st edition), p. 270

顺带一提,国内出版社的数理工书中几乎都存在 d 前不加空格的问题。

d 是否应写作正体

没有定论。

有标准规定应写作正体,但是 UNO 懂个屁 UNO,相当多的文献仍选择斜体 d。开战吧

自动在微分符号前加入适当的空格:

\newcommand\dd{\mathop{}\!\mathrm{d}}
% — 或 —
\newcommand\dd{\mathop{}\!d}

只要将文档中所有微分 d 都换成\dd即可得到正确的空格;而且,当有人强迫你使用正体或斜体 d 时,也能轻松应对。

顺带一提,Office UnicodeMath 中无需上述定义,可直接使用\dd,也能自动得到正确的空格。

单位的字体和间距

\(9.80665\,\rm m/s^2\) 中数字与 m 之间应有空格,但是这个空格太宽也不好,毕竟数值和单位是不可分割的整体。单位用正体,之前加窄空格。在 TeX 中,这个空格同样是\,

299\,792\,458\,\mathrm{m/s} \(299\,792\,458\,\mathrm{m/s}\)

1.5\,\mathrm{k\Omega} \(1.5\,\mathrm{k\Omega}\)

例:上海交通大学物理教研室组编《大学物理教程(第三版)(上册)》p. 193 这是正面案例:m 前加了窄空格。注意下标的 max(非变量名的文字)也正确地采用了正体。 国内出版社的书在排版单位方面做得非常到位。


轻松一下:“本人熟练掌握 LaTeX”

/usepackage[Article]<11tp>
/maketitle
/begin[Document]
/title[Latex is good]
I love latex !
Latex typesets $formulas$ beautifully, like E=mc^2.
/bye

集合的间距

用描述法表示的集合,括号内加上空格会更好看。

\{\,x\mid x=2k,k\in\mathbb{Z}\,\}

\(\{\,x\mid x=2k,k\in\mathbb{Z}\,\}\)

像 {an}、{0, 1} 这样表示的集合就没有这个必要了。

注意上述集合中的竖线以\mid输入,而不是|\vert:前者会在两侧自动加上比\,还要大一点的空格,这样视觉上就很平衡。

例:上海交通大学数学系数学分析课程组编《大学数学·数学分析(上册)》p. 13 不仅该松的地方紧,该紧的地方松(?),有的部分有点短(指集合的竖线),第二个等式还写错了(X 应为 B)。竖线短一点本可增加美感,问题是这竖线比字母 f 还短,两侧也没空格,看上去就像埋在了符号的堆里。

顺带一提,条件概率中的括号和竖线也与之类似处理:

P(\,X=Y\mid XY=1\,) \(P(\,X=Y\mid XY=1\,)\)

竖线大小变

集合中的竖线长度应随着内容增高而增长。

例:上海交通大学数学系编《线性代数(第三版)》p. 143 勉强可以看出大括号适应其中的求和符号而变大了一点,竖线还是太短了。

  • \bigl\bigr系列命令有对应的竖线版\bigm\bigm|会产生和\mid同类的竖线,也就是说,它两侧通常会自动加上稍大的空格。

  • \left\right有对应的竖线版\middle\middle放在\left\right中间,会自动伸展到与它们齐平。\middle|会产生比|更不愿意加空格的竖线,它两边从来都不会有空格。(技术上,\middle前是逗号或分号的时候,之前会有一个小空格。但为什么要在竖线之前留个逗号呢?)

    因此,\middle|用在集合中时,两侧需要手动加上\;(注意,这个空格比\,大)。

\biggl\{x\biggm|\frac{dP}{dx}=0\biggr\} \(\biggl\{x\biggm|\frac{dP}{dx}=0\biggr\}\)

\left\{(x,y)\;\middle|\;\frac{y}{x}=2\right\} \(\left\{(x,y)\;\middle|\;\frac{y}{x}=2\right\}\)

\middle也可以在同一对\left\right中多次出现。

\left<…\middle|…\middle|…\right> \(\left< \phi\, \middle| \frac{\partial^2}{\partial t^2}\middle|\,\psi\right>\)

\(\langle\cdot|{\cdot}|\cdot\rangle\) 的记法一般不需要在竖线两侧加空格,但上式中括号变大使符号视觉上脱离了尖括号,在竖线两侧加了\,来补偿。

绝对值的间距

|+x|=|-x| \(|+x|=|-x|\)

\left|+x\right|=\left|-x\right| \(\left|+x\right|=\left|-x\right|\)

\lvert+x\rvert=\lvert-x\rvert \(\lvert+x\rvert=\lvert-x\rvert\)

第一行明显感觉不太对—— \(|+x|\) 的空格像是 \(I+xI\) 里的那样,而不是 \([+x]\) 里的那样。TeX 真的认为|+x|的意图是把“|”和“x|”相加,因为它分不清|是绝对值的开始还是结束。所以对于绝对值,总是使用\left\right,或明确指出是绝对值开始(\lvert)还是绝对值结束(\rvert)。

例:殳国华、张晴、李丹、邵群、张士文《电子技术实验》p. 90

适用于 Office UnicodeMath,但不适用于 TeX 的内容

UnicodeMath 支持自动识别绝对值:奇数个开始,偶数个结束,所以即使输入|+x|,也能得到正确的间距。

输入||后按空格,可以得到绝对值模板。

记得在每组括号结束后按一下空格,特别是在公式末尾的括号,这样会让 Office 判定绝对值作用的范围并自动伸展括号。

横分式的间距

TeX 没有专门的横分式命令(更没有原生斜分式支持),直接打出/就算作横分式了。这也导致 TeX 中的横分式经常导致间距问题,因为 TeX 把斜线看作普通的变量。

1/\ln x \(1/\ln x\)

1/\!\ln x \(1/\!\ln x\)

1/{\ln x} \(1/{\ln x}\)

若 ln 前真是一变量,如 \(c \ln x\) 中那样,ln 前确得有小空格。TeX 以为“/”就是这样的一个普通变量,于是在 ln 前自动加了空格,但作为分数线的“/”两边一般是不加空格的!结果就是,不得不用\!抵消掉 TeX 自动加的空格。

例:Tristan Needham, Visual Complex Analysis, p. 89 sin、cos、sinh、cosh 前皆能看出有多余的空格。

\!抵消空格总感觉是种很憋屈的方式。将出问题的分式元素用{}包裹初看是曲折的解决方案,但实际上在解决空格问题的同时还在源码中传达了语义。

下面的代码定义\hfrac命令,使之如同\frac一样,但产生间距正确的横分式。

\newcommand\hfrac[2]{%
	\mathinner{\nulldelimiterspace=0pt\left.#1\middle/#2\right.}%
}

来自我花了几分钟糊的。已知 bug:分数内的\left.\right.将失去原先应有的宽度。不保证没有更多的 bug。

分数线大小变

横分式中的分数斜线也应该跟着左右两边的分子分母一起变高。

\frac{a+1}{b}\bigg/\frac{c+1}{d} \(\frac{a+1}{b}\bigg/\frac{c+1}{d}\)

\bigl(x+f(x)\bigr) \big/ \bigl(x-f(x)\bigr) \(\bigl(x+f(x)\bigr)\big/\bigl(x-f(x)\bigr)\)

例:电子技术实验(EE221) 模拟电子技术实验材料 p. 92(PDF 内页码 12) 看起来这来自一本相当古老的书,但是我不知道怎么根据内容找书名。有人知道这是什么书的话,回复指出来吧;有人知道怎么找的话,发个帖教学一下吧 😃

\left.V\middle/\left(1+\dfrac{R_t}{R}\right)\right. \(\left.V\middle/\left(1+\dfrac{R_t}{R}\right)\right.\)

\left.V\!\middle/\left(1+\dfrac{R_t}{R}\right)\right. \(\left.V\!\middle/\left(1+\dfrac{R_t}{R}\right)\right.\)

例:殳国华、张晴、李丹、邵群、张士文《电子技术实验》p. 193

适用于 Office UnicodeMath,但不适用于 TeX 的内容

UnicodeMath 支持多种分数。用通常方法输入竖版分数后,右键单击分子或分母,在弹出的菜单中可以选择 更改为分数(斜式)更改为分数(横式)。通过此种方式(而不是输入分数后不按空格)输入的横分式或斜分式的分数线可以自动适应分子和分母的大小。由于横分式是一体的,不会遇到 TeX 中的横分式间距问题。

\ldiv代替/,用键盘输入横分式。用\sdiv代替/,用键盘输入斜分式。(l 是 linear 的缩写,s 是 skewed 的缩写。)

适用于 MathType 和 Microsoft 公式编辑器,但不适用于 TeX 的内容

Ctrl+F以输入分式。按Ctrl+/以输入斜分式。按Ctrl+T后按Alt+/以输入横分式。先输入分子,后按Tab切换到分母。

由于横分式是一体的,不会遇到 TeX 中的横分式间距问题,大小也会自动适应分子和分母。

直接输入的“/”字符不是分数线,但它具有类似二元运算符的间距设定。


多字母变量的字距

多个字母一起表示单个变量的时候,可视情况用斜体或正体。若采用斜体,一定要用\mathit{…}包裹,否则就像 打 字 带 空 格:

x+difference \(x+difference\)

x+\mathit{difference} \(x+\mathit{difference}\)

x+\text{difference} \(x+\text{difference}\)

例:孟桂娥《C++程序设计实验指导(第二版)》p. 65 要问那字母 p 为何如此宽……为什么呢?

注:原书出现问题的原因可能与数学公式无关,而是字体设定差错,但它很好地展示了字距太大的现象。

上下标的字距

@​wzy1997 在本帖 14 楼提到的那样,虽然像 \(W_2^1\) 这样的情况中下标会挤到字母里面去,但有时候不会自动达成,用\!来手工挤入。

\Gamma_c \quad \Delta^c \(\Gamma_c \quad \Delta^c\)

\Gamma_{\!c} \quad \Delta^{\!c} \(\Gamma_{\!c} \quad \Delta^{\!c}\)

这是因为下标侵入字母实质上使用字符的斜体校正(italic correction)值。正体字母的斜体校正值为零,故默认状态下的大写希腊字母和\mathrm等中的字母皆不会自动调整字母与其下标之间的字距。

也不总是需要这样的手工调整。比如国内高中课本使用的一种排列组合的写法,我觉得就没有调整的必要:

\mathbf{P}_5^3 \(\mathbf{P}_5^3\)

\mathrm{A}_n^k \(\mathrm{A}_n^k\)

若调整成 \(\mathrm{A}_n^{\!k}\) ,看起来就怪怪的。也可能有一些个人喜好的因素在其中吧。

n 重根式的上标位置

TeX 默认会把 n 次方根的 n 放在一个非常诡异的地方。如果根号只有一行,还看不大出来;根号里的式子很大的话就真的很怪。AMSLaTeX 提供了\leftroot\uproot命令来手工调整。

\sqrt[n]{9\frac34} \(\sqrt[n]{9\frac34}\)

\sqrt[\leftroot{-6}\uproot{10}n]{9\frac34}

对一些上标位置不好看的单行根式也可用此法调整——这其实是\leftroot\uproot命令的本意:这两个命令中的数值使用的单位是“方便此种调整的一种小长度单位”(参照* User’s Guide for the amsmath Package*)。

\sqrt[\beta]{k} \(\sqrt[\beta]{k}\)

\sqrt[\leftroot{-2}\uproot{2}\beta]{k}

函数值乘积的间距

两函数值乘积间加上一个小空格,更有助于辨认。

\int_a^b f(x)\,g(x)\,dx \(\int_a^b f(x)\,g(x)\,dx\)

\int_0^t f(x)\,g(t-x)\,dx \(\int_0^t f(x)\,g(t-x)\,dx\)

\(2 \sin(x)\cos(x)\) 这样的场合中不需要手工加空格,因为 TeX 知道 cos 是个函数(\cos),便会自动插入空格。 \(f(x)\,g(x)\) 中的空格必须手动插入,因为 g 事实上只是一个单字母的普通变量。

例:《内卷》 《内卷》 我看到此作品的第一反应是两函数间缺空格,d 前也缺空格。 然后发现两个积分号首尾相接,这是好的,很有艺术感。 最后才发现有什么不对劲。

在极端情况下,TeX 会因不知道自变量何处结束而为预定义函数给出错误的间距。例如,为了避免 \(\log n(\log\log n)^2\) 被理解成 \(\log\bigl(n(\log\log n)^2\bigr)\) ,同样需要手动在第一个* n *之后加空格,来形成视觉上的隔离: \(\log n\,(\log\log n)^2\) 。当然,写成 \((\log n)(\log\log n)^2\) 也可以,如果你能接受多出来的括号的话;或者干脆把第一个 log 写到最后去。

标点符号的间距

数学模式中的句点作为小数点,后通常不会加空格;逗号和分号后通常会自动加上一个小空格;冒号通常作为比例符号使用,两边皆有空格。但是也有相当多的例外:

  • 逗号作为千位分隔符时,其后不加空格。可以用\!抵消自动插入的空格,也可以将逗号包起来写成{,}
  • 冒号作为数学标点使用时(如下面例子中指明函数定义域和上域时),其前不加空格,其后只加小空格。这时,冒号要写成\colon

m^2:2n \(m^2:2n \)

f\colon D\to A \(f\colon D\to A\)

_2F_1(a,b;c;z) \({_2F_1(a,b;c;z)}\)

x=1{,}919{,}810 \(x=1{,}919{,}810\)

例:洪嘉振、刘铸永、杨长俊《理论力学(第 4 版)》p. 74 这本书后边类似的式子冒号的空格都没问题的,就这两页空得出奇地大。

感叹号用在阶乘中,其后默认不加空格,但同自定义函数间一样,连乘中的视觉分隔还是需要的。

\frac{n!}{k!\,(n-k)!} \(\frac{n!}{k!\,(n-k)!}\)

例:冯卫国《概率论与数理统计(第四版)》p. 15 3!3!3!4!4!4! 三!三!三!四!四!四!

最末分号前的空格加上无妨,因为这是上下文句子中的标点符号,而不是数学公式中的标点符号,与数学公式隔开一些也合理。这里不致混淆,无此空格亦可。

Plain TeX 惊为天人地把数学公式中的问号当作是右括号一类的东西,使问号前面不会加空格。若要将问号作函数(你知道 问号函数 吗?)排版,或用在一些小测验问题中,可用{?}

A(4,3)=? \(A(4,3)=?\)

A(4,3)={?} \(A(4,3)={?}\)

?(p/q)+?(p'/q') \({?(p/q)+?(p'/q')}\)

{?}(p/q)+{?}(p'/q') \({?}(p/q)+{?}(p'/q')\)


三种分式与连分式的间距

\frac \(6+\frac{1}{3+\frac{1}{1+\frac{1}{1+\cdots}}}\)

\dfrac \(6+\dfrac{1}{3+\dfrac{1}{1+\dfrac{1}{1+\cdots}}}\)

\cfrac[l]

如果你读的那篇 LaTeX 入门教程没介绍\frac\dfrac(还有\tfrac)的区别,你可能读了一篇假的教程。但 AMSLaTeX 还提供了第三种分式,\cfrac(c 代表 continued,即连分式)。

\cfrac所做之事比\dfrac更增有三:

  • 消除分式右侧间距,使得堆叠的连分式分数线右端对齐在同一竖直线上。
  • 把分子撑大,使之不太靠近上面一条分数线。
  • 支持分子左对齐或右对齐,通过\cfrac[l]\cfrac[r]指定,默认仍居中。

可惜水源所用的 MathJax 没有做上面的第一点和第二点。这不是让\cfrac和连分数失去关系了嘛!(上面演示的效果是我瞎搞硬搞出来的,你可以在右键菜单中找到 TeX 源代码。)

例:Donald E. Knuth, The Art of Computer Programming, 3rd edition, vol. 2, p. 356 \cfrac是 AMSLaTeX 提供的,日常用原版 TeX 的高德纳自己整了个连分数,其他都做了,就是没做分数线右对齐。

国内出版社排版的书里所有公式都是 display style,搞得行距忽小忽大。因其排版相当机械,能排好连分数也只是个巧合罢。

递等式中等号的间距

还记得递等式计算吗?使用 AMSLaTeX 提供的 align 等环境,在等号前插入&,很容易实现在等号处对齐。强调在等号前是因为如果&插入在等号后,会出现间距问题:

\begin{aligned}
  & 1+1+1 \\
= & 2+1 \\
= & 3
\end{aligned}

\[\begin{aligned} & 1+1+1\\ = & 2+1\\ = & 3\end{aligned} \]

\begin{aligned}
    & 1+1+1 \\
={} & 2+1 \\
={} & 3
\end{aligned}

\[\begin{aligned} & 1+1+1\\ ={} & 2+1\\ ={} & 3\end{aligned} \]

如上所示,若对齐点必须在等号后,需要在等号和对齐点间使用一个空盒({})让 TeX 为等号产生正确的间距。

当然,以上描述中的等号也可以是不等号。

例:冯卫国、武爱文《概率统计解题方法与技巧》p. 255 第一行第一个等号左右两侧的空格明显不对称,而其余等号的间距都没有问题。若在 LaTeX 中正常使用 align 等环境,此例中对齐点选在等号左边,并不会出现书中的问题。

国内出版社排版的书对运算符两侧是否要加空格似乎完全没有达成共识:很有可能所有等号两侧都没有空格。我甚至怀疑这些书所用的排版软件需要手动插入所有的空格。

使用\phantom的解法

此问题还有另一种解决方法:仍将对齐点选在等号左边,仅调整缺等号的第一行的位置。

\begin{aligned}
& \mathrel{\phantom{=}} 1+1+1 \\
&= 2+1 \\
&= 3
\end{aligned}

\[\begin{aligned} & \mathrel{\phantom{=}} 1+1+1\\ &= 2+1\\ &= 3\end{aligned} \]

\phantom{…}的作用大致可以理解为设置字体颜色为白色(透明),只是它不会为降低查重率作贡献。

由于等号此时在内部处理时被{}包裹,它便不能自动产生正确的间距(1+1{=}2也不能产生正确的间距),不得不用\mathrel手工重新赋予它等号的属性。若嫌麻烦,也可不用\mathrel,而在\phantom{=}旁边直接加两个\;来补偿。

顺带一提,Office UnicodeMath 也支持\phantom

命题间箭头的间距

对展开书写的命题间的等价关系,可用\iff代替\Longleftrightarrow\Leftrightarrow,不仅好打,还会增加额外的空格,形成视觉层级。\Rightarrow\Leftarrow同理,亦可替换为\implies\impliedby

\((\,l\mathrel{/\mkern-5mu /\mkern-2mu }\varPi\Leftrightarrow\boldsymbol{s}\perp\boldsymbol{n}\,)\)

\((\,l\mathrel{/\mkern-5mu /\mkern-2mu }\varPi\iff\boldsymbol{s}\perp\boldsymbol{n}\,)\)

例:贺才兴《高等数学解题方法与技巧》p. 131 一行里四个字母挤在一起,仿佛是同级的。

例:同上,p. 137

啊对了:没有“不能推出”符号。本来可以用\not\Rightarrow的,但是\not\implies根本没法看( \(\not\implies\) )。可以试试 centernot 宏包,但水源等网站就不支持了。非要用的话,这么玩也凑合:

(A\,\,\,\,\not\!\!\!\!\implies B) \((A\,\,\,\,\not\!\!\!\!\implies B)\)

这就太 hacky 了,不讲武德。


自动生成的间距总结

物质是由原子组成的,TeX 公式也是由原子组成的,物质和公式贴贴。

原子是由原子核和核外角标构成的,原子核决定了原子的种类,核外角标绕核上下分层排布。元素表中共有 13 种元素,根据它们的原子和周围原子之间距离的关系,可将它们归纳为 7 个主族和 1 族零族。TeX 自动插入的空格是由相邻原子种类决定的。

  • 普通原子:拉丁字母、希腊字母和%#./|\□△∂¬∀∃之类的符号
  • 巨算符:∑∏∐∫⋂⋃ sin cos tan log ln exp lim sup inf 等、用\operatorname生成的函数、用\overbrace\underbrace产生的东西
  • 二元运算:+−×÷*⋅∙◇∩∪,还有用\setminus输入的、(\backslash输入的、是普通原子)
  • 二元关系:<=>≈≠∝∈↑↓←⇐⇔⇒→,还有用\mid输入的|(\vert和直接输入的|是普通原子)
  • 左括号:([{⌊⌈⟨,包括\bigl
  • 右括号:⟩⌉⌋}]),包括\bigr,还有!?
  • 标点:,;,还有\colon
  • 里·原子:省略号、分数和由\left\right包裹的公式(单个符号不能自然成为里原子;省略号其实是由三个点组合而成的)

原谅我给 inner atom 起了个中二的译名。

固体中的原子紧密排列,边界之间距离为零。经验表明,液态和气态公式中的原子之间距离只有三种大小:字母 M 宽度的 1/6(窄间距\,)、2/9(中等间距\>\:)、5/18(宽间距\;)。根据相邻两原子的类型,原子之间的距离遵循下表中的规律(参照 The TeXbook, p. 170):

普通 运算 关系 左括 右括 标点
普通原子 (中) (宽) (窄)
巨算符 (宽) (窄)
二元运算 (中) (中) (中) (中)
二元关系 (宽) (宽) (宽) (宽)
左括号
右括号 (中) (宽) (窄)
标点 (窄) (窄) (窄) (窄) (窄) (窄) (窄)
里原子 (窄) (中) (宽) (窄) (窄) (窄)

表中的“–”表示这种情况下的相邻原子不能稳定存在,二元运算原子此时会裂变为普通原子,多余的能量会以中央处理器发热的形式耗散。

当字体大小被压缩时,原子之间的距离被迫减小,一些原子之间的距离保持和字母 M 的宽度成正比,另一些原子之间的距离则突变降为零。上表中有圆括号括起的,便表示受压缩时会发生这种突变的情况。

实验表明,中等间距和宽间距具有一些窄间距没有的性质。它们在文字排版中留有一定可调整的余量,以便将稍微有点长的公式塞进上一行末尾。水源使用的 MathJax 不是 TeX,它不像 TeX 那样自动断行,也没有伸缩空格的能力。

公式原子理论的应用

巨算符-里原子

公式的原子理论成功地解释了之前提到的函数后加\left会导致多余间距的现象。

二元关系-二元关系

在 TeX 公式中直接输入:::=::===等所得到的间距都是正确的,因为两个二元关系会自动连接在一起。

a🅱️:c:d \(a🅱️:c:d\)

例:谢锡麟、倪刚、孙晓光、张祥朝、周雅倩、郑达安《复旦先导讲义:数理基础与程序设计》p. 194 “==”中间的空得太大:双等号本是一个逻辑符号,且此处若真加了空格则程序也不能正常运行。 原因估计是符号是全角的。a+b==c——大概是这样的感觉吧。

顺带一提,这本书在交大图书馆中的索书号是 O1/1,可见 FDU 在交大心中的位置(?)

里原子-里原子

例:上海交通大学化学化工学院大学化学教研室编《大学化学实验》p. 15 这是一张数据表的首列某一格的内容。有无化学大佬讲讲这是啥意思?我猜它应该是想说,NaOH 溶液浓度可以用 m/(MV) 算,而此行的数据单位是 mmol/L——若是这样的话,×10−3应该是×103才对吧。

如果一定要排成这种格式的话,两个连续的分数之间应有一窄空格。TeX 会自动添加这一空格,因为分数是里原子,而参照上表,在正常字体大小下两个里原子之间会有一窄空格。

自定义运算符的间距

有的作者(特别是编程的人们 🐒)喜欢定义一些奇怪的符号当作运算符,比如#$%^&。这些符号在数学中一般不作运算符用,所以往往预设为普通原子。TeX 提供了八种命令,将任何东西转化为指定的原子种类。

  • 转化为普通原子:其实用{}括起来就可以,但 TeX 也提供了\mathord
  • 转化为巨算符:\mathop
  • 转化为二元运算:\mathbin
  • 转化为二元关系:\mathrel
  • 转化为左括号:\mathopen
  • 转化为右括号:\mathclose
  • 转化为标点:\mathpunct
  • 转化为里原子:\mathinner

例如,“%”符号在 C++编程语言中作为二元运算取余数用,而不是取百分数。取余数运算在数学上通常写作* a* mod b,其中的 mod 在 TeX 中写作\bmod。但若必须用%代 mod,需要手动写上\mathbin

Bad: (-9)\%(-4) \((-9)\%(-4)\)

Good: (-9)\mathbin{\%}(-4) \((-9)\mathbin{\%}(-4)\)

Bad: (-9)\operatorname{mod}(-4) \((-9)\operatorname{mod}(-4)\)

Good: (-9)\bmod(-4) \((-9)\bmod(-4)\)

像 mod 这样的以名作符的二元运算处理起来比较复杂。它不是函数,所以用\operatorname不能产生正确的间距。直接用\mathrm亦不能产生间距。用\mathbin{\mathrm{…}}在通常情况下能正确工作,但一旦运算符处在上下标的位置上,根据公式原子理论,其周围的间距也会消失,这不是应有的效果。\bmod的定义在使用\mathbin的同时,抵消了自动插入的间距并自行插入了间距和断行点。

例:翁慧玉、俞勇《C++程序设计:思想与方法(慕课版)(第 3 版)》p. 29 全半角括号混用让本就不合理的间距更加乱七八糟。

随 堂 考

TeX 公式完全可以用来排版程序代码!

\[\begin{align} & \{\\ & \qquad i=\mathrm{0x5F3759DF}-(i\mathbin{\gg}1);\\ & \qquad x=*(\mathbf{float}\,*)\,i;\\ & \qquad x=x*(\mathrm{1.5f}-\mathit{x\_half}*x*x);\\ & \qquad\vdots\\ & \} \end{align} \]

高德纳本人也搞这种,毕竟是计算机科学家。

上面的例子中,*本就是二元运算符,>>则被设置为了二元运算符。根据#$%^&这几个符号在 C++中的用途,简单说说它们分别可能作为何种原子出现在公式中。

参考答案

打程序代码显然不是公式功能的本意,即使设置了对应的原子类型,也当然会在稍微极端的情况下遇到间距问题,以下所述仅供参考。

  • #:预处理器中使用,可作为一元(字符串化)或二元运算符(标记粘贴)、指令的开头。根据场合,使用\#\mathbin{\#\#}等。
  • $:这在 C++中没有特殊含义,可能可以作为标识符名使用。若如此,与整个标识符一起设为斜体是个不错的主意:\text{\itshape …\$…}。或者,与整个标识符一起保持正体。
  • %:二元取模运算,作独立的运算符使用时为\mathbin{\%},作复合赋值运算符时为\mathrel{\%}。注意a \mathrel{\%}= b中,%会自动和=紧密连在一起。
  • ^:二元异或运算,同上。^无法直接在数学模式下打出,可用\hbox{\^{}}\mbox{\textasciicircum}等。
  • &:一元取地址运算、二元位与运算、引用类型。根据场合,使用\mathbin{\&}\mathrel{\&}=\&等。

P.S. 如果你觉得这道题出得莫名其妙的话,这就是我做大雾卷子的感受……


未完待续。未完待续。