我以为我以前可以做类似的事情,但我找不到解决方案。
我想按如下方式格式化我的大数字:
12,504.3124 2359 3
我知道我可以用来siunitx
部分设置它,但我没有得到我想要的结果:
\documentclass{article}
\usepackage{siunitx}
\newcommand\aenumA[1][4]{%%
\num[group-separator={,},
group-digits=integer,
]}
\newcommand\aenumB[1][4]{%%
\num[group-digits=decimal,
]}
\pagestyle{empty}
\begin{document}
\aenumA{12504.312423593}
\aenumB{12504.312423593}
\end{document}
我想要一个混合解决方案(整数部分使用逗号,小数部分使用空格)。我实际上还希望能够在两者之间进行不同的分组:整数部分每三位数字分组,小数部分每四位数字分组。
有没有直接的方法来实现这一点siunitx
?或者,我需要在小数点处拆分数字,然后分别处理这两个部分,然后胶水让他们复合吧?
答案1
完全可扩展的解(分两步扩展,并扩展其数值参数)。
此更新使用\,
在四位数字组之间插入空格(因此空格在数学模式下也可见)。该宏成功处理了诸如\aenum {}
或 之类的边缘情况\aenum {.}
。它检测到前导+
或-
符号。它可以用作 \aenum{\A.\B}
if\A
并\B
扩展为数字字符串,或者\aenum{\C}
if\C
扩展为整数或小数。
可以修改代码以插入任何想要的内容。为了允许用户指定分隔符,我将首先重复使用此处的代码,将数字分组为连续的括号组,然后使用实用程序\xintListWithSep
(xinttools
另一种可能的方法是插入一些宏作为分隔符,即 \sepINT
, \sepDEC
但这不如拥有想要的明确标记而不需要进一步扩展那么干净)。甚至可以让用户指定的分隔符成为可选参数(在括号内)并保持可扩展性,但这会增加一层复杂性。
上段前半部分概述的更通用的方法已在本答案底部的代码示例中实现
\documentclass{article}
\usepackage{xinttools}% for token manipulation utilities
\makeatletter
% THIS IS A COMPLETELY EXPANDABLE MACRO
%
% We use _ as private letter.
% We use \@gobble, \@firstoftwo, \@secondoftwo
% although xinttools has
% \xint_gobble_i = \@gobble
% \xint_firstoftwo = \@firstoftwo
% \xint_secondoftwo = \@secondoftwo
\catcode`_ 11
\makeatletter
% The argument is (left) expanded, it may be a macro.
% (even input such as \A.\B is allowed)
%
% A - or + sign is correctly identified.
% It may contain or not contain a decimal mark `.'
% If the number has a `.' both the integer and decimal parts
% are allowed to be empty.
% \aenum{.} and \aenum {} are both allowed.
\newcommand{\aenum}[1]
{\romannumeral0\expandafter\aenum@aa\romannumeral-`0#1..\relax}
% helper macro (there are similar ones in xint)
\def\xint_minusplusfork #1-+#2#3\krof {#2}
% to deal with numbers starting either with +, with -, or without a sign.
\def\aenum@aa #1{\xint_minusplusfork
#1+{\expandafter-\romannumeral0\aenum@a}%
-#1{\expandafter+\romannumeral0\aenum@a}%
-+{\aenum@a #1}%
\krof }
% We need to fork according to whether there is a decimal mark or none.
% decimal mark present: #3=. (and #2 is authorized to be empty)
% decimal mark absent: #3=empty
\def\aenum@a #1.#2.#3\relax
{\if\relax #3\relax\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi
{\aenum@comma@three@l {#1}}%
{\expandafter\aenum@b\expandafter
{\romannumeral0\aenum@comma@three@l {#1}}%
{\romannumeral0\aenum@space@four@r {#2}}}%
}
\def\aenum@b #1#2{\expandafter\aenum@c\expandafter{#2}{#1}}
\def\aenum@c #1#2{ #2.#1}
%% GROUPING BY THREE FROM THE RIGHT TOWARDS THE LEFT
%% AND INSERTING , AS SEPARATOR
\def\aenum@comma@three@l #1%
{\expandafter\xintreverseorder\expandafter
{\romannumeral0\aenum@comma@three@r{\xintReverseOrder{#1}}}}
\def\aenum@comma@three@r #1{\expandafter\aenum@comma@three@ra
\romannumeral-`0#1\relax\relax\relax\dummy }
% we here check here if "short", for technical reasons
\def\aenum@comma@three@ra #1#2#3{\ifx #3\relax
\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi
{\aenum@comma@three@rshort #1#2#3}{\aenum@comma@three@rb {#1#2#3}}}
% initial space to feed the \romannumeral0. The #1 may be empty.
\def\aenum@comma@three@rshort #1\relax #2\dummy { #1}
\def\aenum@comma@three@rb #1#2#3#4{\ifx #4\relax
\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi
{\aenum@comma@three@rc {#1}#2#3#4}{\aenum@comma@three@rb {#1,#2#3#4}}}
\def\aenum@comma@three@rc #1#2\relax #3\dummy {\if\relax #2\relax
\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi % Notice that #1 is never empty in this branch
{ #1}{ #1,#2}}% initial space to feed the \romannumeral0
%% GROUPING BY FOUR GOING FROM THE LEFT TO THE RIGHT
%% We also expand the decimal part which thus could have been so far a
%% macro.
% THIS VERSION USES '\,' AS SEPARATOR.
\def\aenum@space@four@r #1{\expandafter\aenum@space@four@ra
\romannumeral-`0#1\relax\relax\relax\relax\dummy }
% we first check here if "short", for technical reasons
\def\aenum@space@four@ra #1#2#3#4{\ifx #4\relax
\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi
{\aenum@space@four@rshort #1#2#3#4}{\aenum@space@four@rb {#1#2#3#4}}}
% initial space to feed the \romannumeral0. The #1 may be empty.
\def\aenum@space@four@rshort #1\relax #2\dummy { #1}
\def\aenum@space@four@rb #1#2#3#4#5{\ifx #5\relax
\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi
{\aenum@space@four@rc {#1}#2#3#4#5}{\aenum@space@four@rb {#1\,#2#3#4#5}}}
\def\aenum@space@four@rc #1#2\relax #3\dummy {\if\relax #2\relax
\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi % Notice that #1 is never empty in this branch
{ #1}{ #1\,#2}}% initial space to feed the \romannumeral0
\makeatother
\catcode`_ 8
\begin{document}
Fringe case:
X\aenum{}Y\aenum{+}Z\aenum{-}T\aenum{.}Y\aenum{+.}Z\aenum{-.}T
\aenum{1.2}\quad \aenum{1.23}\quad \aenum{1.234}\quad \aenum{1.2345}\quad \aenum{1.23456}\quad \aenum{1.234567}\quad \aenum{1.2345678}
\aenum{-12.3}\quad \aenum{-123.3}\quad \aenum{-1234.3}\quad \aenum{-12345.3}\quad \aenum{-123456.3}\quad \aenum{-1234567.3}
Math mode:
$\aenum{+12504.312423593}$
$\aenum{-12504.312423593}$
$\aenum{-1250412504.312423593312423593}$
$\aenum{+12345678912345678912345.12345678123456781234567}$
\end{document}
更通用的方法。它定义了\aegroupbythreeL
和,\aegroupbyfourR
可扩展地将标记从右侧开始按三个三个分组,分别从左侧开始按四个四个分组。然后它使用\xintListWithSep
fromxinttools
插入任意指定的分隔符。
此类“分组”宏可能会被添加到未来版本中xinttools
。
\documentclass{article}
\usepackage{xinttools}% for \xintLength macro only
\makeatletter
% We use _ as private letter.
% We use \@gobble, \@firstoftwo, \@secondoftwo for easing up reading the
% code to people not used to the xinttools names:
% \xint_gobble_i = \@gobble
% \xint_firstoftwo = \@firstoftwo
% \xint_secondoftwo = \@secondoftwo
\catcode`_ 11
% DEFINITION of a completely expandable macro (expands in two steps)
% \aenumwithsep with three arguments:
% The number argument is expanded, it may be a macro.
% (and even input such as \A.\B is allowed)
%
% A - or + sign is correctly identified.
% It may contain or not contain a decimal mark `.'
% If the number has a `.' both the integer and decimal parts
% are allowed to be empty.
% THIS VARIANT \aenumwithsep HAS THREE ARGUMENTS:
% #1 = separator for integer part
% #2 = separator for decimal part
% #3 = actual number, may be empty or just a dot. May start with an
% optional + or - sign.
\newcommand {\aenumwithsep}[3]
{\romannumeral0\expandafter\aenumwithsep@aa\romannumeral-`0#3..\relax
{#1}{#2}}
% helper macro (there are similar ones in xint)
\def\xint_minusplusfork #1-+#2#3\krof {#2}
% to deal with numbers starting either with +, with -, or without a sign.
\def\aenumwithsep@aa #1{\xint_minusplusfork
#1+{\expandafter-\romannumeral0\aenumwithsep@a}%
-#1{\expandafter+\romannumeral0\aenumwithsep@a}%
-+{\aenumwithsep@a #1}%
\krof }
% We need to fork according to whether there is a decimal mark or none.
% decimal mark present: #3=. (and #2 is authorized to be empty)
% decimal mark absent: #3=empty
\def\aenumwithsep@a #1.#2.#3\relax #4#5%
{\if\relax #3\relax\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi
{\xintlistwithsep {#4}{\aegroupbythreeL {#1}}}%
{\expandafter\aenumwithsep@b\expandafter
{\romannumeral0\xintlistwithsep{#4}{\aegroupbythreeL {#1}}}%
{\romannumeral0\xintlistwithsep{#5}{\aegroupbyfourR {#2}}}}%
}
\def\aenumwithsep@b #1#2{\expandafter\aenumwithsep@c\expandafter{#2}{#1}}
\def\aenumwithsep@c #1#2{ #2.#1}
%% GROUPING BY THREE FROM THE RIGHT TOWARDS THE LEFT
%% 1234567-->{1}{234}{567}
%% Uses `\xintLength` to get the length. First we expand the argument
%% although not needed here. But in case the macro is used elsewhere, we
%% define it as a macro expanding in two steps and expanding its
%% argument (from the left)
\def\aegroupbythreeL #1{\romannumeral0\expandafter\aegroupbythreeL@aa
\romannumeral-`0#1\relax }
\def\aegroupbythreeL@aa #1\relax
{\expandafter\aegroupbythree@a\expandafter
{\romannumeral-`0\xintLength{#1}}#1\relax\relax\relax
}
\def\aegroupbythree@a #1{\ifnum #1>\thr@@
\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi
{\ifcase\numexpr #1-(#1/\thr@@)*\thr@@\relax
\expandafter\aegroupbythree@A\or
\expandafter\aegroupbythree@B\else
\expandafter\aegroupbythree@C\fi }%
{\ifnum #1>\z@
\expandafter\aegroupbythree@short
\else\expandafter\aegroupbythree@empty
\fi }}
\def\aegroupbythree@short #1\relax\relax\relax{{#1}}
\def\aegroupbythree@empty \relax\relax\relax{ }% Return empty list, and
% not list with one empty braced item
\def\aegroupbythree@A #1#2#3{\aegroupbythree@b {{#1#2#3}}}
\def\aegroupbythree@B #1{\aegroupbythree@b {{#1}}}
\def\aegroupbythree@C #1#2{\aegroupbythree@b {{#1#2}}}
\def\aegroupbythree@b #1#2#3#4{\ifx #4\relax
\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi
{#1}{\aegroupbythree@b {#1{#2#3#4}}}}
%% GROUPING BY FOUR GOING FROM THE LEFT TO THE RIGHT
%% 1234567-->{1234}{567}
\def\aegroupbyfourR #1{\romannumeral0\expandafter\aegroupbyfourR@a
\romannumeral-`0#1\relax\relax\relax\relax\dummy }
% we first check here if "short", for technical reasons
\def\aegroupbyfourR@a #1#2#3#4{\ifx #4\relax
\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi
{\aegroupbyfourR@short #1#2#3#4}{\aegroupbyfourR@b {{#1#2#3#4}}}}
% space to feed the \romannumeral0. We need to check for emptiness
%
\def\aegroupbyfourR@short #1\relax #2\dummy {\if\relax #1\relax
\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi { }{{#1}}}
\def\aegroupbyfourR@b #1#2#3#4#5{\ifx #5\relax
\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi
{\aegroupbyfourR@c {#1}#2#3#4#5}{\aegroupbyfourR@b {#1{#2#3#4#5}}}}
\def\aegroupbyfourR@c #1#2\relax #3\dummy {\if\relax #2\relax
\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi % Notice that #1 is never empty in this branch
{#1}{#1{#2}}}
\makeatother
\catcode`_ 8
\newcommand{\aenum}[1]{\aenumwithsep {,}{\,}{#1}}
\begin{document}
Fringe cases:
X\aenum{}Y\aenum{+}Z\aenum{-}T\aenum{.}Y\aenum{+.}Z\aenum{-.}T
\aenum{1.2}\quad \aenum{1.23}\quad \aenum{1.234}\quad \aenum{1.2345}\quad \aenum{1.23456}\quad \aenum{1.234567}\quad \aenum{1.2345678}
\aenum{-12.3}\quad \aenum{-123.3}\quad \aenum{-1234.3}\quad \aenum{-12345.3}\quad \aenum{-123456.3}\quad \aenum{-1234567.3}
Math mode:
$\aenum{+12504.312423593}$
$\aenum{-12504.312423593}$
$\aenum{-1250412504.312423593312423593}$
$\aenum{+12345678912345678912345.12345678123456781234567}$
$\aenum {1234}\neq\aenum{12345}\neq\aenum{123456}\neq\aenum{1234567}$
With separators $!$ and $?$:
$\aenumwithsep !?{+1234567.1234567}$
$\aenumwithsep !?{+12345678912345678912345.12345678123456781234567}$
Testing expansion
\begin{verbatim}
\expandafter\expandafter\expandafter
\def
\expandafter\expandafter\expandafter
\x
\expandafter\expandafter\expandafter
{\aenumwithsep \a\b{+12345678912345678912345.12345678123456781234567}}
\ttfamily\meaning\x
\end{verbatim}
\expandafter\expandafter\expandafter
\def
\expandafter\expandafter\expandafter
\x
\expandafter\expandafter\expandafter
{\aenumwithsep \a\b{+12345678912345678912345.12345678123456781234567}}
\ttfamily\meaning\x
(spaces after \string\a\space and \string\b\space come from \string\meaning)
% \expandafter\expandafter\expandafter
% \def
% \expandafter\expandafter\expandafter
% \x
% \expandafter\expandafter\expandafter
% {\aenumwithsep !?{.}}
% \ttfamily\meaning\x+++
\end{document}
答案2
使用来自的代码https://tex.stackexchange.com/a/171012/4427
\documentclass{article}
\usepackage{xparse,l3regex}
\ExplSyntaxOn
\NewDocumentCommand{\aenum}{ >{\SplitArgument{1}{.}} m }
{
\aenumaux#1
}
\NewDocumentCommand{\aenumaux}{mm}
{
\ae_add_commas:n { #1 }
\IfValueT{#2} % no decimal part
{ . \ae_add_spaces:n { #2 } }
}
\tl_new:N \l__ae_number_temp_tl
\cs_new_protected:Npn \ae_add_commas:n #1
{
\tl_set:Nn \l__ae_number_temp_tl { #1 }
% we need to start from the end, so we reverse the string
\tl_reverse:N \l__ae_number_temp_tl
% add a comma after any group of three tokens
\regex_replace_all:nnN { (.{3}) } { \1\, } \l__ae_number_temp_tl
% if the length of the string is a multiple of three a trailing comma is added
% so we remove it
\regex_replace_once:nnN { \,\Z } { } \l__ae_number_temp_tl
% reverse back
\tl_reverse:N \l__ae_number_temp_tl
\tl_use:N \l__ae_number_temp_tl
}
\cs_new_protected:Npn \ae_add_spaces:n #1
{
\tl_set:Nn \l__ae_number_temp_tl { #1 }
\regex_replace_all:nnN { (.{4}) } { \1\cS\ } \l__ae_number_temp_tl
\regex_replace_once:nnN { \cS.\Z } { } \l__ae_number_temp_tl
\tl_use:N \l__ae_number_temp_tl
}
\ExplSyntaxOff
\begin{document}
\aenum{12504}
\aenum{12504.312423593}
\aenum{0.12345678}
\end{document}
答案3
以下示例使group-separator
包siunitx
依赖于开关\ifSiunitxDecimal
,该开关指示是否达到小数部分。开关设置为 false。当output-decimal-marker
使用时,开关设置为 true。当打印数字后再次将开关重置为 false 时,黑客部分就出现了。这是通过转义两个组级别来实现的\aftergroup
。
\documentclass{article}
\usepackage{siunitx}
\newif\ifSiunitxDecimal
\protected\def\SiunitxDecimalfalse{\global\let\ifSiunitxDecimal\iffalse}
\protected\def\SiunitxDecimaltrue{\global\let\ifSiunitxDecimal\iftrue}
\sisetup{
output-decimal-marker={.\SiunitxDecimaltrue
\aftergroup\aftergroup\aftergroup\SiunitxDecimalfalse
},
group-separator={\ifSiunitxDecimal\,\else{,}\fi},
group-minimum-digits=3,
retain-explicit-plus,
}
\begin{document}
$\num{+12504.312423593}\quad\num{-12345.54321}$\quad
\num{123456789}\quad\num{.987654321}
\end{document}