I am a little annoyed with the inconsistency of the spacing of some math symbols like + and - when they appear to the left of a letter (or symbol) and when they appear between two letters (or symbols), as shown in the picture. Is there a reason for these spacing to be different? Is there any way to make them always equal without me having to do it manually every time they appear in the text?
3 Answers
Fully automatic, thanks to LuaTeX.
\documentclass{article}
\usepackage{amsmath}
\usepackage{luacode}
\begin{luacode*}
local muglue_subtype
do
for num, name in pairs(node.subtypes(node.id("glue"))) do
if name == "muglue" then
muglue_subtype = num
break
end
end
end
assert(muglue_subtype, "No such subtype!")
function table.contains(haystack, needle)
for _, straw in next, haystack do
if straw == needle then
return true
end
end
return false
end
local glue_fields = { "width", "stretch", "stretch_order", "shrink", "shrink_order" }
local function binordspacing(head,style,penalties)
local cur = head
local match = 0
while cur do
local subtype = node.subtypes(cur.id)[cur.subtype]
if match == 0 and table.contains({"op", "bin", "rel", "open", "punct"}, subtype) then
match = 1
elseif match == 1 and subtype == "bin" then
match = 2
elseif match == 2 and subtype == "ord" then
local binordspacing = tex.getmath("binordspacing", style)
local n = node.new("glue", muglue_subtype)
for _, field in ipairs(glue_fields) do
n[field] = binordspacing[field]
end
head = node.insert_before(head, cur, n)
match = 0
else
match = 0
end
cur = cur.next
end
return true
end
luatexbase.add_to_callback("pre_mlist_to_hlist_filter", binordspacing, "binordspacing")
\end{luacode*}
\begin{document}
\begin{equation*}
\alpha = -p(-w_1-v_0) - g(w_1)
\end{equation*}
\begin{center}
$\alpha = -p(-w_1-v_0) - g(w_1)$
\end{center}
\end{document}
Ahhh, my eyes!
In the comments you mentioned that you'd also like to have space between the opening parenthesis and the minus (oh god, why?) so I came up with a more configurable approach. This allows you to insert random spacing in places where TeX would not consider that (for good reasons).
\documentclass{article}
\usepackage{amsmath}
\usepackage{luacode}
\begin{luacode*}
local muglue_subtype
do
for num, name in pairs(node.subtypes(node.id("glue"))) do
if name == "muglue" then
muglue_subtype = num
break
end
end
end
assert(muglue_subtype, "No such subtype!")
local glue = glue or {}
function glue.copy(src)
local glue_fields = { "width", "stretch", "stretch_order", "shrink", "shrink_order" }
local g = node.new("glue", muglue_subtype)
for _, field in ipairs(glue_fields) do
g[field] = src[field]
end
return g
end
local knuth_table = {
ord = { ord = "0" , op = "1" , bin = "(2)", rel = "(3)", open = "0" , close = "0" , punct = "0" , inner = "(1)" },
op = { ord = "1" , op = "1" , bin = "*" , rel = "(3)", open = "0" , close = "0" , punct = "0" , inner = "(1)" },
bin = { ord = "(2)", op = "(2)", bin = "*" , rel = "*" , open = "(2)", close = "*" , punct = "*" , inner = "(2)" },
rel = { ord = "(3)", op = "(3)", bin = "*" , rel = "0" , open = "(3)", close = "0" , punct = "0" , inner = "(3)" },
open = { ord = "0" , op = "0" , bin = "*" , rel = "0" , open = "0" , close = "0" , punct = "0" , inner = "0 " },
close = { ord = "0" , op = "1" , bin = "(2)", rel = "(3)", open = "0" , close = "0" , punct = "0" , inner = "(1)" },
punct = { ord = "(1)", op = "(1)", bin = "*" , rel = "(1)", open = "(1)", close = "(1)", punct = "(1)", inner = "(1)" },
inner = { ord = "(1)", op = "1" , bin = "(2)", rel = "(3)", open = "(1)", close = "0" , punct = "(1)", inner = "(1)" }
}
local function subtype(n)
if not n then
return nil
end
return node.subtypes(n.id)[n.subtype]
end
local function traverse(head, style)
for n in node.traverse(head) do
if n.id == node.id("sub_mlist") then
traverse(n.list, style)
else
if subtype(n) == "bin" then
local undefined = false
-- look at the previous
local prevtype = subtype(n.prev)
if knuth_table[prevtype] and knuth_table[prevtype].bin == "*" then
undefined = true
local g = glue.copy(tex.getmath(prevtype .. "binspacing", style))
head = node.insert_before(head, n, g)
end
-- look at the next
local nexttype = subtype(n.next)
if undefined or knuth_table.bin[nexttype] == "*" then
local g = glue.copy(tex.getmath("bin" .. nexttype .. "spacing", style))
head = node.insert_after(head, n, g)
end
undefined = false
end
end
end
end
local function binordspacing(head,style,penalties)
traverse(head, style)
return true
end
luatexbase.add_to_callback("pre_mlist_to_hlist_filter", binordspacing, "binordspacing")
\end{luacode*}
% Add further abominations to this list
\Umathopenbinspacing\displaystyle=\thinmuskip
\Umathopenbinspacing\textstyle=\thinmuskip
\Umathbinordspacing\displaystyle=\thinmuskip
\Umathbinordspacing\textstyle=\thinmuskip
\begin{document}
\begin{equation*}
\alpha = -p(-w_1-v_0) - g(w_1)
\end{equation*}
\begin{center}
$\alpha = -p(-w_1-v_0) - g(w_1)$
\end{center}
\end{document}
-
17Maybe, just maybe, giving the customers what they want isn't always right… :-)– MicoMar 16, 2020 at 6:55
-
1@Gustavo I forgot to set the subtype of the glue to muglue. See updated answer. Mar 17, 2020 at 8:18
-
3@Gustavo is not "much better". It is horrible. I really hope that I never have to read a document with this settings. Mar 17, 2020 at 9:59
-
1@HenriMenke For current LaTeX versions, I would recommend using
pre_mlist_to_hlist_filter
instead ofmlist_to_hlist
. Then you just have to replacereturn node.mlist_to_hlist(...)
withreturn true
and you magically restore compatibility with all packages which usemlist_to_hlist
. Mar 17, 2020 at 10:15 -
3Please don't teach peple to misuse LaTeX to create horrible typography. Pretty please.– vonbrandMar 17, 2020 at 18:42
You ask,
Is there a reason for these spacing[s] to be different?
Indeed there is! What you've (re-)discovered is the difference between -
(and +
too) being employed as either a unary
or a binary
operator.
If the spacing were the same for both types of operators, you would probably get annoyed that it is the same.
In essence, what you're observing is the result of centuries of fine math typography, which has established that making the spacing around unary and binary operators the same is not optimal.
Here are some practical examples of the application of spacing rules for unary and binary operators.
\documentclass{article}
\begin{document}
\obeylines % just for this example
``$-$'' and ``$+$'' treated as unary operators:
$\dots,-3,-2,-1,0,+1,+2,+3,\dots$
$-1 \ne +1$
\bigskip
``$-$'' and ``$+$'' treated as binary operators:
$\dots,{}-3,{}-2,{}-1,0,{}+1,{}+2,{}+3,\dots$ --- looks awful!
$1+1=2$ \quad $2-2=0$
\bigskip
Mixed use:
$-1=5-6$ \quad $-5+6=+1$
\end{document}
-
5
Welcome! This is actually a feature and not a bug. The first minus is a sign while the second one is a binary operator. If you want the larger spacing, you can add a {}
.
\documentclass{article}
\usepackage{amsmath}
\begin{document}
\begin{align*}
\alpha&=-p(-w_1-v0)-g(w_1)\\
\alpha&={}-p(-w_1-v0)-g(w_1)\\
\end{align*}
\end{document}
But to me it seems more reasonable to treat signs different from operators.
-
2
-
1@barbarabeeton I agree. Which is why I wrote "But to me it seems more reasonable to treat signs different from operators.". (Seems that one gets many more positive votes if one uses "unary" instead of sign...)– user194703Mar 16, 2020 at 15:09
-
-
@Gustavo Yes. Did you find one where it does not work?– user194703Mar 17, 2020 at 4:39
-
-
symbols. Interestingly, the lack of whitespace around the unary-
symbol located between(
andw_1
doesn't seem to bother you -- or at least not as much as does the first unary minus symbol.