单位 (siunitx)

“9.81 m/s²” 中数字与单位之间的那个空格,并不是为了好看。国际计量大会发布的《SI 手册》明确指出:这个空格就是乘号——正因如此,一个量是不可拆分的整体,绝不能在行末断开。在 LaTeX 中把这一整套规则接管过去的,就是 siunitx 宏包。本页沿着当前 v3 的三根支柱 \num\unit\qty 展开,并顺带回答:单位为什么排成直立体,千位为什么用空格而不是逗号分隔,表格中的 S 列如何按小数点对齐,以及旧帖子里的 \SI\si 该怎么改写。

为什么不能手写 \mathrm{kg}

手写之所以会失败,是因为要遵守的不是个人品味,而是 国际标准,而且有三条规则同时生效。第一条是字体。《SI 手册》规定,单位符号“无论周围文字使用何种字体,都排成直立体”,因为 mkgs 不是变量。但 LaTeX 数学模式中的字母默认是斜体,于是直接写 $9.81 kg$ 会让单位看起来像斜体 kg 的乘积。写成 \mathrm{kg} 可以修好这一处——而一篇论文里有几十处这样的地方。

第二条是空格。《SI 手册》§5.4.3 规定“数值总是在单位之前,并且总是用一个空格把二者分开”,接着解释说 这个空格被视为乘号。因此 9.81kg9.81 kg 都不合规。标准给出的唯一例外,是平面角的度、分、秒(°)——只有这里不留空格。所以 30.2 °C(摄氏温度有空格)与 22.20°(角度无空格)会出现在同一段里。也就是说,一篇文档中“° 前留空格”和“不留空格”两种情况并存,要在几百处手工维持这个区别,说实话并不现实。

第三条是“不可断开”。既然量是数值与单位的乘积,就不允许出现行末只剩 9.81m/s² 被推到下一行的排法。siunitx 默认禁止这种断行,只有在确实需要时才用 allow-quantity-breaks 打开。所以在窄栏中看到 underfull hbox 警告,那不是事故,而是规则正在生效的证据。以上三点,只要在导言区加一行 \usepackage{siunitx} 就一并解决——而且还附带一个好处:日后改一处设置,就能改变整篇文档的呈现方式。

latex
\usepackage{siunitx}

% by hand: three separate things to remember, every time
$9.81\,\mathrm{m}\,\mathrm{s}^{-2}$

% with siunitx: one command, and the standard is applied for you
\qty{9.81}{\metre\per\second\squared}
\qty{100}{\degreeCelsius}   % 100 °C   - space kept before the degree
\qty{10}{\percent}          % 10 %     - space here too
\ang{22.20}                 % 22.20°   - but no space for a plane angle

\num:裸写的数字会发生什么

\num{…} 只接收数值本身,并按标准处理分组、指数、符号和前导零。\num{12345} 得到 12 345\num{1.234e5} 得到 1.234 × 10⁵\num{.5} 得到 0.5。最后一项作用不小:《SI 手册》要求“当数介于 +1 与 −1 之间时,小数点前必须有一个零”,所以应写 0.234 而非 .234。源文件里敲 .5、输出里得到 0.5,省下的键入量比看上去多。

千位用 空格而非逗号 分隔,并不是宏包作者的个人偏好。这是 1948 年第 9 届国际计量大会第 7 号决议定下的:数可以每三位分成一组以便阅读,而“组与组之间的空隙中,绝不插入点或逗号”。同一决议在谈到小数点时用的措辞是“逗号(法国惯例)或点(英国惯例)”——今天读来颇有几分可爱。所以 43 279.168 29 合规,43,279.168,29 不合规。siunitx 中 group-separator 的默认值,正是那个细空格 \,

输入端反而很宽容。input-decimal-markers 的默认值是 ., 两个字符,所以 \num{0,1234}\num{0.1234} 结果相同。指数标记 input-exponent-markers 的默认值是 dDeE 四个字母,因此源自 Fortran 风格的 3.45d-4 也会被读作 3.45 × 10⁻⁴。这意味着从 CSV 或仪器日志里直接粘来的数值,不必先统一格式就能扔进去。\num 唯一不做的是乘法:在 v3 中,数值之间的乘积交给专用的 \numproduct{1.2 x 3.4}(用 x 分隔)。这种分工同样遵循标准——《SI 手册》规定,数与数相乘时只应使用乘号 ×。

latex
\num{12345}        % 12 345      - grouped with a thin space, never a comma
\num{43279.16829}  % 43 279.168 29
\num{1.234e5}      % 1.234 x 10^5
\num{3.45d-4}      % 3.45 x 10^-4  - Fortran-style d also works
\num{0,1234}       % 0.1234       - comma accepted on input
\num{.5}           % 0.5          - leading zero supplied
\num{-15673}       % -15 673
\numproduct{1.2 x 3.4}   % 1.2 x 3.4   - products need their own command

小数点用点还是逗号:localeoutput-decimal-marker

标准的答案是:两者都对。2003 年第 22 届国际计量大会第 10 号决议规定,小数标记“应为行上的点或行上的逗号”,且所选的那一种“应是该语言与语境中习惯使用的那一种”。因此,在德语、法语、西班牙语文档中排成 9,81 m/s²,与在英语文档中排成 9.81 m/s²,具有完全相同的正当性。并不存在一个是“正版”、另一个是“口音”的问题。siunitx 的设计对此很诚实:它把输入的格式与输出的格式分开。

切换方式有两种。要精细控制,就分别设置输出小数标记 output-decimal-marker 与千位分隔 group-separator。要整套切换地区惯例,则用 locale 选项;宏包自带 UK、US、DE(德国)、PL(波兰)、FR(法国)、SI(斯洛文尼亚)、ZA(南非)的配置。关键在于源文件无需改动:\qty[locale = DE]{6.789}{\metre} 源里仍写点号,输出却是 6,789 m。正因如此,同一份稿件才能同时出英文版和德文版。

对于宏包未收录的地区——例如西班牙语区或意大利语区——locale 帮不上忙,需要自己写:\sisetup{output-decimal-marker = {,}}。这里有一个很容易踩的坑:把小数标记改成逗号之后,又把千位分隔也设成逗号。同时写下 group-separator = {,}output-decimal-marker = {,}\num{12345.678} 会输出 12,345,678,读者根本分不清哪个逗号是小数点。这时你就会切身体会到,1948 年的决议为何要特意写明“组与组之间绝不插入点或逗号”。把千位分隔保留为默认的细空格,才是稳妥做法。

latex
\qty[locale = DE]{6.789}{\metre}       % 6,789 m   - source keeps the point
\num[output-decimal-marker = {,}]{1.23}  % 1,23
\num[group-separator = {,}]{12345}       % 12,345    - US/UK habit, not SI

% document-wide, for a locale siunitx does not ship
\sisetup{output-decimal-marker = {,}}    % group-separator stays a thin space

% do NOT do this: the output 12,345,678 is unreadable
% \sisetup{output-decimal-marker = {,}, group-separator = {,}}

\unit:单位的两种写法与 \per

\unit{…} 只排版单位本身,写法有两种。literal(直写) 风格直接敲符号,如 \unit{kg.m/s^2},其中句点 . 是单位之间的乘积,/ 是商,^ 是上标。 风格则用前缀、单位和幂逐词搭建,如 \unit{\kilo\gram\metre\per\second\squared},写起来很像把单位名称念出来。实用上的分界很清楚:临时赶写一个单位就用 literal;日后可能想一次性改变全文单位外观,就用宏。因为选项只对宏形式起作用。

含义输出
\gram基本单位宏;\metre \second \ampere \kelvin \mole \candela 同理g
\kilo前缀;不能单独使用,后面必须跟单位k
\kilogram带前缀的单位写成一个词;与 \kilo\gram 结果相同kg
\per把后面的单位送入分母:\joule\per\moleJ mol⁻¹
\squared把前一个单位平方(后置);立方用 \cubed
\square把后一个单位平方(前置);立方用 \cubic
\degreeCelsius摄氏度;按标准要求,前面保留空格°C
\percent百分号;SI 认可可与之并用,前面同样有空格%

\per 的外观由 per-mode 选项决定——这里正是旧教程最常弄错的地方:默认是负幂\unit{\metre\per\second\squared} 输出的是 m s⁻² 而不是 m/s²\unit{\kilo\gram\per\cubic\metre} 也是 kg m⁻³ 而不是 kg/m³。想要斜杠就写 per-mode = symbol,想要上下排列的分数就写 per-mode = fraction。在导言区写一次 \sisetup{per-mode = symbol},全文所有单位都会遵循这一方针。

一旦选了斜杠,siunitx 会悄悄补上括号。因为《SI 手册》§5.4.6 规定,用斜线作除法时必须加括号以避免歧义:(a/b)/c 可以,a/b/c 不行。所以 \unit[per-mode = symbol]{\joule\per\mole\per\kelvin} 输出的是 J/(mol K) 而不是 J/mol/K。一条手写时只能靠“记得留意第二个斜杠”来遵守的规则,就这样被结构性地保证了。手册还补充说,复杂情形下更宜使用负指数——这正是默认值为 per-mode = power 的原因。

latex
\unit{kg.m/s^2}                          % kg m/s^2   - literal input, slash kept
\unit{\kilo\gram\metre\per\second\squared}   % kg m s^-2  - default per-mode = power
\unit[per-mode = symbol]{\kilo\gram\per\cubic\metre}   % kg/m^3

\unit{\joule\per\mole\per\kelvin}                      % J mol^-1 K^-1
\unit[per-mode = symbol]{\joule\per\mole\per\kelvin}   % J/(mol K)  - brackets added

\sisetup{per-mode = symbol}   % one policy for the whole document

\qty 把数值和单位合到一起:范围、列表与角度

\qty{数值}{单位} 把第一个参数交给 \num,第二个交给 \unit,并在两者之间放上符合标准的空格。因此不必学新记法:能写进 \num 的和能写进 \unit 的,直接并排放好即可。单位部分既可以是宏形式(\metre\per\second\squared),也可以是 literal 形式(m/s)。本页开头那个想法——量是数值与单位的乘积——被 \qty 直接做成了一条命令。

范围和列表也有各自的命令,其中最惹眼的是 \qtyrange{10}{20}{\metre} 的输出:它给出 10 m to 20 m,而不是 10–20 m。这同样源自标准。《SI 手册》提醒,用短横线连接物理量可能被误读为负值;由于量是单一的数学实体,用连接号连接数值再只写一个单位同样不正确;并 强烈建议使用“to”这个词。在表头里写 10–20 m 的冲动很强,但那是不合规的。\qtylist{10;20;30}{\metre} 排出的是 10 m, 20 m and 30 m

角度是唯一交给 \ang 而非 \qty 处理的情形。如前所述,\ang{45} 输出没有空格的 45°;用分号分隔度、分、秒时,\ang{45;30;15} 得到 45° 30′ 15″。不过默认应优先用前一种写法:《SI 手册》指出,平面角一般更宜将度作十进制细分,因此除航海、制图、天文以及极小角度测量等领域外,22.20° 优于 22° 12′。分与秒的写法,不妨看作只给需要它的领域准备的工具。

latex
\qty{9.81}{\metre\per\second\squared}   % 9.81 m s^-2
\qty{1.234e5}{\kilo\gram\per\cubic\metre}  % 1.234 x 10^5 kg m^-3
\qty{2.998e8}{m/s}                       % literal units work here too

\qtyrange{10}{20}{\metre}    % 10 m to 20 m       - the word "to", not a dash
\qtylist{10;20;30}{\metre}   % 10 m, 20 m and 30 m

\ang{22.20}       % 22.20°     - decimal degrees are preferred
\ang{45;30;15}    % 45° 30' 15"  - degrees; minutes; seconds

用括号写不确定度:\num{1.234(5)}

测量值总带着不确定度。siunitx 会把输入中的圆括号直接读作不确定度,因此 \num{1.234(5)} 排出 1.234(5)。这种括号记法不是 siunitx 的发明,而是《SI 手册》§5.4.5 给出的写法——那里把中子质量写作 mₙ = 1.674 927 471(21) × 10⁻²⁷ kg,括号中的数字表示相对于所引数值末几位的标准不确定度。所以,把 (…) 交给 \num,正是标准本来就预期的输入写法。

呈现方式由 uncertainty-mode 选择。默认的 compact 把括号中的数字当作最低位上的数字打印(\num{123.45(120)}123.45(120)),full 则把不确定度打印为完整数值(→ 123.45(1.20))。启用 separate-uncertainty 会得到用 \pm 分开的形式 1.234 ± 0.005。这里值得留意的是单位作用于谁:写成 12.3 ± 0.4 kg±0.4 看起来就像没有单位。siunitx 对此心里有数:\qty[separate-uncertainty]{12.3(4)}{\kilogram} 会自动补上括号,输出 (12.3 ± 0.4) kg

latex
\num{1.234(5)}                              % 1.234(5)
\num{123.45(120)}                           % 123.45(120)  - compact, the default
\num[uncertainty-mode = full]{123.45(120)}  % 123.45(1.20)
\num[separate-uncertainty]{1.234(5)}        % 1.234 +- 0.005

\qty{12.3(4)}{\kilogram}                        % 12.3(4) kg
\qty[separate-uncertainty]{12.3(4)}{\kilogram}  % (12.3 +- 0.4) kg  - brackets added

让表格按小数点对齐:S 列与 table-format

在列格式中用 S 代替 clr,该列的数字就会按小数点对齐。单元格内容由与 \num 相同的解析器读取,因此 1.2e3 变成 1.2 × 10³,用逗号写的 975,31 变成 975.31——从 CSV 灌进来的一列会自己理顺。这同样是在回应标准的要求:《SI 手册》为此只写了一句话,但毫不含糊——表格中的数,在同一列内所用格式不应变化。

第一次踩坑几乎总在列标题上。把文字放进 S 列的单元格,siunitx 会试图把它读成数值的一部分,所以 标题要用花括号包起来,写成 {Mass}——忘了这一步,那一行的对齐就会塌掉。带单位或脚注标记的标题同理:整个保护起来,写成 {Mass / \unit{\gram}}。要稳定列宽,就用 table-format 声明“整数位数.小数位数”。S[table-format = 4.1] 会预先为 4 位整数和 1 位小数留出空间,所以之后再加行也不会让列的位置移动。带不确定度时写 S[table-format = 2.2(1), separate-uncertainty],带指数时写 S[table-format = 1.3e2]

latex
\begin{tabular}{@{}cS[table-format = 4.1]@{}}
  \toprule
  Entry & {Mass / \unit{\gram}} \\   % braces protect the heading text
  \midrule
  1 & 1234.5 \\
  2 &   12.0 \\
  3 &    6.7 \\
  \bottomrule
\end{tabular}

% uncertainties and exponents get their own formats
% S[table-format = 2.2(1), separate-uncertainty]  ->  12.34 +- 0.05
% S[table-format = 1.3e2]

在标题里写单位也有定式:不是 Length [m],而是 Length / m,即除以单位。原因是记法冲突——在数学中 [x] 表示“x 的量纲”。把方括号挪用作单位的容器,[a] 就会在“加速度的量纲”和“单位是 m/s²”之间产生歧义。除以单位之后,表格正文只剩无量纲的数,恰好与 S 列的职责相合。另外,若想在 \multicolumn\multirow 内获得同样的对齐,请使用 S 列的宏版本 \tablenum[table-format = …]{…}

\SI\qty 的区别:从 v2 迁移到 v3

它们是同一件工作的旧名与新名:\SI 属于 v2,\qty 属于 v3。siunitx 的作者 Joseph Wright 在 2021 年 5 月 17 日发布的 v3.0.0 中从内部重写了宏包,并把文档命令的名字改得“更具描述性”。手册指出,\SI\si\SIrange\SIlist 仍然可用,但 不推荐用于新文档,应改用 \qty… 系列。只有 \num\ang 的名字在这次变更中保持不变。

v2(旧名)v3(现行)作用
\SI\qty{9.81}{\metre\per\second\squared}数值与单位
\si\unit{\kilo\gram}仅单位
\SIrange\qtyrange{10}{20}{\metre}范围(10 m to 20 m
\SIlist\qtylist{10;20;30}{\metre}列表(10 m, 20 m and 30 m
\num\num{1.234e5}(名称不变)仅数值
\ang\ang{22.20}(名称不变)平面角

v3 还把输入收紧了一些。最显眼的是前缀:不带单位的前缀不再被接受,\unit{\kilo} 会以 ! Package siunitx Error: Found prefix part with no unit. 中止。这不是刁难,而是向标准看齐——《SI 手册》§5.2 早就规定,前缀绝不单独使用,也不使用复合前缀。也就是说,v3 中看似“破坏性变更”的部分,至少有一些其实是宏包在追赶规则。基于同样的方针,数值之间的乘积移到了 \numproduct,量之间的乘积移到了 \qtyproduct,复数则交给专用的 \complexnum

如果旧文档实在无法在 v3 下编译,可以在载入时指定版本以调用旧版行为——\usepackage{siunitx}[=v2]。但请把它当作续命手段。v3 会在内部把许多旧选项名映射到新名称,并每次在日志里留下一条警告。日志里排满 siunitx 警告的文档,其实是在提醒你:该改名了。若是新开一份文档,要记的只有三个命令:\num\unit\qty——只有数值、只有单位、二者兼有。本页谈到的所有标准细节,都在这三个命令内部处理完毕。

latex
% v2 names: still work, no longer recommended for new documents
\SI{9.81}{\metre\per\second\squared}
\si{\kilo\gram}

% v3 names: use these
\qty{9.81}{\metre\per\second\squared}
\unit{\kilo\gram}

% a prefix on its own is now an error:
% ! Package siunitx Error: Found prefix part with no unit.
\unit{\kilo}

% last resort for a document that will not build with v3
\usepackage{siunitx}[=v2]