log류 함수와 mod

\mathrm{sin}\sin은 지면 위에서 똑같은 “sin”으로 보입니다. 그래도 앞의 것은 틀렸고, 그 차이는 3.33325pt 입니다. LaTeX에서 $a\operatorname{op}b$의 너비를 재면 23.46638pt, $a\mathrm{op}b$는 20.13313pt입니다. 직립체라는 겉모습은 같지만 \mathrm 쪽은 앞뒤 간격을 가지고 있지 않습니다. 이 3.33325pt는 장식이 아니라, TeX이 Op 원자(atom) 의 좌우에 넣는 얇은 간격 두 개분에 정확히 해당합니다. 이 페이지에서는 그 원자 이야기에서 출발해, 내장된 32개의 함수 이름이 LaTeX 소스 어디에 적혀 있는지, 왜 그중 정확히 10개만 아래첨자를 바로 밑에 두는지, \DeclareMathOperator의 별표가 무엇을 바꾸는지, 그리고 \bmod, \pmod, \mod, \pod라는 네 가지 mod 표기를 실측값으로 가려냅니다.

\mathrm{sin}이 틀린 이유: 원자의 종류가 간격을 정합니다

수학 모드에서는 영문자 한 글자 한 글자가 변수 이름으로 받아들여져 수학 이탤릭으로 조판됩니다. 그래서 sin x라고 쓰면 “사인”이 아니라 s, i, n, x 네 양의 곱이 나옵니다. 여기까지는 잘 알려진 이야기이고, 많은 사람이 대처법으로 \mathrm{sin}을 떠올립니다. 직립체가 되면 해결된 것처럼 보이기 때문입니다. 그런데 \mathrm이 바꾸는 것은 글꼴뿐 이고, TeX이 보기에 원자의 종류는 여전히 Ord, 곧 보통 기호입니다.

TeX은 수식의 구성 요소를 Ord(보통), Op(연산자), Bin(이항 연산자), Rel(관계 기호) 등의 원자 종류 로 분류하고, 이웃한 종류의 조합만 보고 간격을 정합니다. Ord와 Ord 사이에는 간격이 없습니다. Op와 Ord 사이에는 얇은 간격(\thinmuskip, 3mu) 이 들어갑니다. \sin은 Op, \mathrm{sin}은 Ord이므로 차이가 생깁니다. 다음 넷을 10pt 본문에서 재면 그 구조가 그대로 숫자로 드러납니다.

measured on TeX Live 2024, 10pt article
\DeclareMathOperator{\myop}{op}
% \sbox0{$...$}\message{\the\wd0}

$a\operatorname{op}b$        23.46638pt
$a\myop b$                   23.46638pt   % identical to \operatorname
$a\mathop{\mathrm{op}}b$     23.46638pt   % same font as \mathrm, but Op class
$a\mathrm{op}b$              20.13313pt   % 3.33325pt narrower

$\mkern3mu$  1.66663pt        % one thin space; two of them = 3.33326pt

세 번째 줄이 결정적입니다. \mathop{\mathrm{op}}은 글꼴로는 \mathrm과 완전히 같은 것을 쓰면서도 너비는 \operatorname과 10만분의 1pt까지 일치합니다. 즉 차이를 만드는 것은 글꼴이 아니라 \mathop이 부여하는 Op라는 종류 입니다. 그리고 3.33325pt의 차이는 1.66663pt짜리 얇은 간격 정확히 두 개분입니다. a와 “op” 사이에 하나, “op”와 b 사이에 하나입니다. “\mathrm은 간격이 어긋난다”는 이야기가 이렇게 소수점 아래까지 설명됩니다. 실무적 결론은 단순합니다. 함수 이름은 반드시 전용 명령이나 \operatorname으로 쓰고 \mathrm은 쓰지 않는다 는 것입니다.

내장 함수 이름 32개는 어디에 적혀 있는가

\log이나 \sin의 정의는 패키지가 아니라 LaTeX 본체의 포맷 파일 latex.ltx 에 있습니다. TeX Live 2024에서는 12487행부터 12518행까지 정확히 32줄 이며, \DeclareRobustCommand\log{\mathop{\operator@font log}\nolimits} 형태가 늘어서 있을 뿐입니다. 이 목록은 더 오래되어, 커누스의 plain.tex 1058~1085행에 거의 그대로 들어 있습니다. 즉 \sin을 쓸 수 있는 것은 amsmath 덕분이 아니라 추가 패키지 없이 처음부터 쓸 수 있는 것입니다(직접 만들 때만 amsmath가 필요하며, 뒤에서 다룹니다).

  • 삼각함수: \sin \cos \tan \cot \sec \csc, 역함수 \arcsin \arccos \arctan.
  • 쌍곡선함수: \sinh \cosh \tanh \coth. \sech\csch들어 있지 않으므로 필요하면 직접 선언합니다.
  • 로그와 지수: \log(일반), \ln(자연로그), \lg(밑이 2, 정보이론에서 자주 씀), \exp.
  • 대수・기하 등: \deg(차수), \dim(차원), \ker(핵), \hom, \arg(편각), \det(행렬식).
  • 첨자가 바로 아래에 놓이는 10개: \lim \limsup \liminf \max \min \sup \inf \det \Pr \gcd.

밑과 지수는 일반적인 첨자 기능으로 그대로 붙습니다. \log_2 x는 “log”의 오른쪽 아래에 2를, \sin^2\theta는 오른쪽 위에 2를 붙여 익숙한 sin²θ가 됩니다. 원전을 읽어야 알아채는 소소한 사실 하나. \limsup의 정의는 \mathop{\operator@font lim\,sup}으로, “lim”과 “sup” 사이에 \, 얇은 간격이 손으로 들어가 있습니다. 한 덩어리 단어가 아니라 두 단어를 일부러 조금 떼어 놓고 조판한 것입니다.

latex
\[
  \sin^2\theta + \cos^2\theta = 1, \qquad
  \log_2 8 = 3, \qquad \ln e = 1.
\]

첨자가 바로 아래 놓이는 10개, 그리고 \limits가 듣지 않는 이유

32개 가운데 22개는 정의 끝에 \nolimits가 붙어 첨자가 기호의 오른쪽 아래 에 나옵니다. 나머지 10개——\lim \limsup \liminf \max \min \sup \inf \det \Pr \gcd——에는 \nolimits적혀 있지 않습니다. \mathop의 기본값이 디스플레이 스타일에서 “첨자를 바로 아래”이므로, 이 10개만 별행 수식에서 \lim 아래에 x→0을 둡니다. 극한이나 최댓값은 “어떤 범위에서”를 크게 보여야 한다는 수학 관례가 정의에 그대로 나타난 것입니다. 인라인(텍스트 스타일)에서는 10개도 오른쪽 아래에 붙습니다.

latex
\[
  \lim_{x \to 0} \frac{\sin x}{x} = 1, \qquad
  \max_{1 \le i \le n} a_i .
\]

여기서 \sum이나 \int에 듣는 \limits를 함수 이름에도 쓰면 되지 않겠느냐고 생각하기 쉽습니다. 그런데 amsmath를 불러온 상태에서는 \log\limits_{k}가 아무 일도 일으키지 않습니다. 오류도 경고도 없이 조용히 무시됩니다. 이유는 amsopn.sty 27행에 있습니다. \def\nolimits@{\@ifnextchar\limits{\nolimits\@gobble}{\nolimits}} 입니다. 연산자 이름의 정의는 끝에서 이 \nolimits@를 부르는데, 다음 토큰이 \limits이면 \nolimits를 내보낸 뒤 그 \limits를 먹어 버립니다. 실측으로도 확인됩니다.

measured on TeX Live 2024, 10pt article
% depth of the box tells us where the script went (bigger = below the operator)

% WITHOUT amsmath — \log is \mathop{...}\nolimits, and \limits overrides it
$\displaystyle\log_{k}x$          d = 2.44443pt
$\displaystyle\log\limits_{k}x$   d = 9.47220pt   % moved below

% WITH amsmath — the \limits is swallowed by \nolimits@
$\displaystyle\log_{k}x$          d = 2.44443pt
$\displaystyle\log\limits_{k}x$   d = 2.44443pt   % unchanged: nothing happened

\limits의 일반 규칙(\sum 같은 대형 연산자에 쓸 수 있다는 것, 자세한 내용은 “합, 적분, 대형 연산자” 참조)은 amsmath 아래의 함수 이름에는 적용되지 않습니다. 반대 방향인 \nolimits는 그대로 통과하므로 \lim\nolimits_{k}\lim의 첨자를 오른쪽 아래로 밀어낼 수는 있습니다. 첨자를 바로 아래에 두고 싶을 때의 올바른 답은 \limits가 아니라 다음 절의 별표 선언 입니다.

\DeclareMathOperator\operatorname: 별표가 정하는 것

목록에 없는 이름——부호 함수 sgn, 대각합 tr, 계수 rank, ess sup, argmax——을 제대로 조판하고 싶을 때가 amsmath의 차례입니다. 한 번만 쓸 것이면 \operatorname{sgn} x, 반복해서 쓸 것이면 프리앰블에서 \DeclareMathOperator{\sgn}{sgn}으로 선언 합니다. 이 페이지 첫머리의 실측대로, 선언한 \sgn의 너비는 \operatorname{sgn}과 완전히 같습니다. 둘은 같은 \qopname을 부르는, 말 그대로 같은 것이기 때문입니다. 선언의 이점은 품질이 아니라 한곳에 모인다 는 데 있습니다. 나중에 표기를 바꾸고 싶으면 프리앰블 한 줄만 고치면 됩니다.

preamble
\usepackage{amsmath}
\DeclareMathOperator{\sgn}{sgn}            % scripts to the right, like \log
\DeclareMathOperator*{\argmax}{arg\,max}   % scripts underneath, like \lim

% in the body:
% \[ \sgn x, \qquad \argmax_{x \in S} f(x) \]

별표가 정하는 것은 첨자의 위치뿐입니다. 별표가 없으면 \log 계열(오른쪽 아래), 별표가 있으면 \lim 계열(별행 수식에서 바로 아래)이 됩니다. 한 번만 쓸 때는 \operatorname*{…}이 같은 일을 합니다. 이것도 치수로 확인됩니다. 같은 op를 별행 수식으로 조판하고 _{n\to\infty}를 붙였을 때 상자의 깊이는 별표 없이 2.44443pt, 별표를 붙이면 8.94444pt입니다. 늘어난 깊이가 바로 기호 아래에 매달린 첨자입니다. 이름에 \,를 넣은 것(arg\,max)은 \limsup의 정의와 같은 이유로, 두 단어 사이에 알맞은 간격을 남기기 위해서입니다.

amsmath는 상·하극한 주변 어휘도 더해 줍니다. \varlimsup\varliminf는 “lim” 위(아래)에 가로줄을 그은 변형으로, \limsup의 “lim sup”이라는 철자보다 기호적입니다. \injlim\projlim은 순극한과 역극한을 “inj lim”, “proj lim”으로 조판하고, \varinjlim\varprojlim은 “lim” 아래에 →(또는 ←)를 깐 범주론에서 익숙한 모양이 됩니다. 모두 별표 선언과 마찬가지로 첨자가 바로 아래 에 놓입니다. 어느 것을 고를지는 분야의 관례를 따르는 편이 안전하며, 망설여진다면 투고처의 스타일 파일이 이미 정의해 두지 않았는지 확인하세요.

\bmod \pmod \mod \pod: 네 가지 mod를 실측으로 가리기

mod 표기가 헷갈리는 이유는 네 가지가 모두 “mod”를 찍어 내면서 간격과 괄호만 다르기 때문입니다. \bmod\pmod는 표준 LaTeX에, \mod\pod는 amsmath에 있습니다. 설계의 차이는 정의를 보면 한눈에 드러납니다. \bmod만이 \mathbin{…}으로 정의되어 있는데, 곧 +-와 같은 Bin(이항 연산자) 원자 라는 뜻이고, 나머지 셋은 앞에 간격을 두고 내용을 늘어놓을 뿐인 이른바 후치 주석입니다.

amsmath.sty, lines 905-912
\DeclareRobustCommand{\bmod}{\nonscript\mskip-\medmuskip\mkern5mu\mathbin
  {\operator@font mod}\penalty900
  \mkern5mu\nonscript\mskip-\medmuskip}
\DeclareRobustCommand{\pod}[1]{\allowbreak
  \if@display\mkern18mu\else\mkern8mu\fi(#1)}
\DeclareRobustCommand{\pmod}[1]{\pod{{\operator@font mod}\mkern6mu#1}}
\DeclareRobustCommand{\mod}[1]{\allowbreak\if@display\mkern18mu
  \else\mkern12mu\fi{\operator@font mod}\,\,#1}

\if@display에 주목하세요. \pod\pmod별행 수식이면 18mu, 본문 안이면 8mu 를, \mod18mu와 12mu 를 앞에 둡니다. \bmod에는 이 분기가 없습니다. 이항 연산자로서 좌우 대칭으로 \mkern5mu를 놓을 뿐이라, 별행이든 본문 안이든 너비가 변하지 않는 유일한 하나 입니다. 같은 식 a … n을 10pt로 재면 설계가 그대로 숫자로 나타납니다.

명령출력너비 본문 → 별행쓰는 곳
a \bmod na mod n36.01039pt → 36.01039pt(불변)나머지를 으로 쓸 때. Bin 원자라 앞뒤가 대칭입니다. 표준 LaTeX
a \pmod{n}a (mod n)46.01036pt → 51.56578pt합동식 x \equiv y \pmod{n} 끝에 붙이는 표준형. 표준 LaTeX
a \mod{n}a mod n40.45473pt → 43.78798pt괄호를 꺼리는 방식의 합동식에. \bmod보다 앞 간격이 넓습니다. amsmath 필요
a \pod{n}a (n)23.51038pt → 29.06580pt“mod”를 빼고 괄호만. 법이 문맥상 자명할 때 씁니다. amsmath 필요

별행 너비에서 본문 너비를 빼면 정의가 정확히 재현됩니다. \pmod\pod의 차이는 5.55542pt로, 18mu − 8mu = 10mu이며 10pt에서는 9.99976 − 4.44434 = 5.55542pt입니다. \mod의 차이는 3.33325pt로 18mu − 12mu = 6mu에 해당합니다. \bmod의 차이는 0입니다. 무엇을 쓸지는 의미가 정합니다. 나머지라는 값 자체 를 쓸 때는 \bmod(5 \bmod 3 = 2), 두 수가 법 n에서 합동임 을 주장할 때는 \pmod(17 \equiv 5 \pmod{12})입니다. 이 둘을 뒤바꾸면 독자에게 “값”과 “관계”의 구별이 전달되지 않습니다.

latex
\[
  5 \bmod 3 = 2, \qquad
  17 \equiv 5 \pmod{12}.
\]