自动转换数字以使用正确的 SI 单位前缀

自动转换数字以使用正确的 SI 单位前缀

假设一个人正在谈论数字10^610^7、等等,他也可以把它们写成10^8、、、以及它们相应的10^91M10M100M1GSI 单位前缀

有没有办法将表格上的数字转换为

 10000000...
 1E6 
 1E-6
-100000... 

自动转换为带有 SI 单位前缀的数字?

------------------------------------
  Input              |      Output
------------------------------------
 \prefix{1000000}    |         1M
 \prefix{10000000}   |        10M
 \prefix{100000000}  |       100M
 \prefix{1000000000} |         1G
 \prefix{1E6}        |         1M
 \prefix{-1E6}       |         1μ
------------------------------------

答案1

这允许输入任何(当然是有效的)值或表达式。代码(ab)使用 LaTeX3 浮点单元来解析输入表达式并将其转换为指数表示法。这样,代码只需查找要应用于该数字的正确前缀。范围之外的数字会打印更多数字,以使数字适合最接近的前缀(IE, 1E28印刷品10000 Y1E-28印刷品0.0001 y)。

\prefix(可能应该称为\postfix:-) 是可扩展的,接受一个参数,即数字,并打印它后面跟着的前缀。

\prefixSI宏不可扩展,且具有与siunitx\SI宏相同的签名。使用(实际上)\prefixSI评估其第一个强制参数,如上所述,并将其传递给。选定的前缀存储在(组内,因此\prefix\nebu_prefix:n\SI\prefix \prefix可以再次使用),它可用于单位表达式,例如\prefixSI{1000}{\prefix\metre}打印1 km

前缀可以用 来声明\setprefix \<siunitx prefix macro> { <prefix> } { <value> },例如\setprefix \centi { c } { -2 }。确保 存在\<siunitx prefix macro>。如果您不打算使用,\prefixSI那么您可以\relax在那里使用。

而且,由于代码使用 的expl3浮点单元来解析数字,因此您不限于aEb或十进制表示法。您可以执行在l3fp(看这里以查看列表)、类似a*10^b或任何其他内容。

\documentclass{article}
\usepackage{siunitx}
\ExplSyntaxOn
% Internals
\int_new:N \l__nebu_min_prefix_int
\tl_new:N \l__nebu_base_number_tl
\tl_new:N \l__nebu_mode_tl
\cs_new:Npn \nebu_prefix:n #1
  {
    \exp_args:Nf \__nebu_prefix:n
      { \exp_args:Nf \fp_to_scientific:n { \tl_lower_case:n {#1} } }
  }
\cs_new:Npn \__nebu_prefix:n #1
  { \__nebu_prefix:nwnw #1 \q_stop }
\cs_new:Npn \__nebu_prefix:nwnw #1 e #2 \q_stop
  { \__nebu_find_prefix:nnn {#2} {#1} {1} }
\cs_new:Npn \__nebu_find_prefix:nnn #1 #2 #3
  {
    \tl_if_exist:cT { l__nebu_ #1 \tl_use:N \l__nebu_mode_tl _prefix_tl }
      {
        \use_i_delimit_by_q_stop:nw
          { \__nebu_output:nnn {#2} {#1} {#3} }
      }
    \int_compare:nNnT {#1} < \l__nebu_min_prefix_int
      {
        \use_i_delimit_by_q_stop:nw
          {
            \exp_args:Nf \__nebu_find_prefix:nnn
              { \int_eval:n { \l__nebu_min_prefix_int } } {#2}
              { #3 / 1\prg_replicate:nn { \l__nebu_min_prefix_int - #1 }{ 0 } }
          }
      }
    \use_i:nn
      {
        \exp_args:Nf \__nebu_find_prefix:nnn
          { \int_eval:n {#1-1} } {#2} {#3*10}
      }
      \q_stop
  }
\exp_args_generate:n { fv }
\cs_new:Npn \__nebu_output:nnn #1 #2 #3
  {
    \exp_args:Nfv \nebu_output:nn
      { \fp_to_decimal:n {#1*#3} } { l__nebu_ #2 \tl_use:N \l__nebu_mode_tl _prefix_tl }
  }
\cs_new_protected:Npn \nebu_prefix_set:Nnn #1 #2 #3
  {
    \exp_args:Nxx \__nebu_prefix_set:nnN
      { \tl_trim_spaces:n {#2} } { \int_eval:n {#3} } #1
  }
\cs_new_protected:Npn \__nebu_prefix_set:nnN #1 #2 #3
  {
    \tl_clear_new:c { l__nebu_ #2 _prefix_tl }
    \tl_clear_new:c { l__nebu_ #2 _siunitx_prefix_tl }
    \tl_set:cn { l__nebu_ #2 _prefix_tl } {#1}
    \tl_set:cn { l__nebu_ #2 _siunitx_prefix_tl } {#3}
    \int_set:Nn \l__nebu_min_prefix_int { \int_min:nn {#2} { \l__nebu_min_prefix_int } }
  }
% User interface
\NewExpandableDocumentCommand \prefix { m }
  { \nebu_prefix:n {#1} }
\cs_new:Npn \nebu_output:nn #1 #2 { #1\, \textrm{#2} }
\NewDocumentCommand \setprefix { m m m }
  { \nebu_prefix_set:Nnn #1 {#2} {#3} }
\DeclareSIPrefix \none { } { 0 }
\setprefix \none { } { 0 }
%
\cs_new_protected:Npn \__nebu_store:nn #1 #2
  {
    \tl_set:Nn \l__nebu_base_number_tl {#1}
    \cs_set:Npn \prefix {#2}
  }
\NewExpandableDocumentCommand \prefixSI { o m o m }
  {
    \group_begin:
      \cs_set_eq:NN \nebu_output:nn \__nebu_store:nn
      \tl_set:Nn \l__nebu_mode_tl { _siunitx }
      \nebu_prefix:n {#2}
      \SI [#1] { \l__nebu_base_number_tl } [#3] {#4}
    \group_end:
  }
\ExplSyntaxOff

\setprefix \yocto { y } { -24 }
\setprefix \zepto { z } { -21 }
\setprefix \atto  { a } { -18 }
\setprefix \femto { f } { -15 }
\setprefix \pico  { p } { -12 }
\setprefix \nano  { n } { -9 }
\setprefix \micro { \SIUnitSymbolMicro } { -6 }
\setprefix \milli { m } { -3 }
\setprefix \centi { c } { -2 }
\setprefix \deci  { d } { -1 }
\setprefix \deca  { da } { 1 }
\setprefix \hecto { h }  { 2 }
\setprefix \kilo  { k }  { 3 }
\setprefix \mega  { M }  { 6 }
\setprefix \giga  { G }  { 9 }
\setprefix \tera  { T }  { 12 }
\setprefix \peta  { P }  { 15 }
\setprefix \exa   { E }  { 18 }
\setprefix \zetta { Z }  { 21 }
\setprefix \yotta { Y }  { 24 }

\begin{document}
\prefix{1}\par
\prefix{4E6}\par
\prefix{1E-15}\par
\prefix{1e-6}\par
\prefix{300}\par
\prefix{1000000}\par
\prefix{.001}\par
\prefix{.000004}\par
\prefix{1E5}\par
\prefix{10000}\par
\prefix{.0001}\par
\prefix{5E7}\par
\prefix{1E-5}\par
\prefix{3E8}\par
\prefix{1E-4}\par
\prefix{1E-24}\par
\prefix{1E-28}\par
\prefix{1E24}\par
\prefix{1E28}\par
\prefix{1e3*pi}\par
\prefixSI{1e3*pi}{\prefix\metre}\par
\end{document}

在此处输入图片描述

答案2

已编辑以解决两种输入形式。已编辑以便在科学计数法中允许使用大写或小写 E(例如1E61e6)。

已编辑,可自动辨别标准单位上的乘数 1、10 或 100。

如果前缀超出 1E-24 到 1E26 的范围,则??输出 a。

补充用于更普遍的乘数使用(1、10、100 之外)。

由于这个答案已经通过 EDITS 进行了扩展,让我解释一下逻辑。有 3 个宏按顺序相互调用。让我从头到尾讲解。最低级别的宏是

\prefixSN[<multiplier>]{1E<SI-prefix-integer>}

乘数默认为 1,但可以是 10 或 100。SI 前缀整数是定义了 SI 前缀的特定整数(-24、-21、...-6、-3、-2、-1、0、1、2、3、6、...21、24)。如果提供的整数不在此列表中,则输出 a ??。如果整数在列表中,则在乘数后输出相应的 SI 前缀。

\prefixSN输入正确语法的宏被调用\prefixmult,语法为

\prefixmult{1E<integer>}

此处的整数应在 -24 到 26 的范围内。根据整数的值,它将前缀设置为 1、10 或 100,并将整数调整为下一个较低的 SI 标准值。例如,如果的输入是\prefixmult1E5它将设置调用\prefixSI[100]{1E3}

顶级宏,\prefix提供\prefixmult其整数格式,可以接受四种可能形式的输入:

\prefix{1E<integer>}
\prefix{1e<integer>}
\prefix{10<...>0}
\prefix{.0<...>01}

它通过查找E或进行排序e。找到一个后,它将大写 E 形式\prefixmult与相关整数一起传递给 。如果没有E找到,它会查找小数点、1和多个0标记。根据找到的内容,它会根据零的数量重建相关整数并将其传递给\prefixmult

这是 MWE。

\documentclass{article}
\usepackage{siunitx,listofitems}
\newcommand\prefixSN[2][1]{#1\,\prefixSNaux#2\relax}
\def\prefixSNaux 1E#1\relax{%
  \ifnum#1=0\relax\else
  \ifnum#1=-1\relax\textrm{d}\else  
  \ifnum#1=-2\relax\textrm{c}\else  
  \ifnum#1=-3\relax\textrm{m}\else  
  \ifnum#1=-6\relax\textrm{\ensuremath{\mu}}\else  
  \ifnum#1=-9\relax\textrm{n}\else  
  \ifnum#1=-12\relax\textrm{p}\else  
  \ifnum#1=-15\relax\textrm{f}\else  
  \ifnum#1=-18\relax\textrm{a}\else  
  \ifnum#1=-21\relax\textrm{z}\else  
  \ifnum#1=-24\relax\textrm{y}\else  
  \ifnum#1=1\relax\textrm{da}\else  
  \ifnum#1=2\relax\textrm{h}\else  
  \ifnum#1=3\relax\textrm{k}\else  
  \ifnum#1=6\relax\textrm{M}\else  
  \ifnum#1=9\relax\textrm{G}\else  
  \ifnum#1=12\relax\textrm{T}\else  
  \ifnum#1=15\relax\textrm{P}\else  
  \ifnum#1=18\relax\textrm{E}\else  
  \ifnum#1=21\relax\textrm{Z}\else  
  \ifnum#1=24\relax\textrm{Y}\else 
  ??%
  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
}
\newcommand\prefix[1]{%
  \setsepchar{1E||1e}%
  \readlist\scinot{#1}%
  \ifnum\scinotlen>1\relax
    \itemtomacro\scinot[2]\temp%
    \expandafter\prefixmult\expandafter{\expandafter1\expandafter E\temp}%
  \else
    \setsepchar{./1/0}%
    \readlist\zerocount{#1}%
    \ifnum\zerocountlen=1\relax
      \expandafter\prefixmult\expandafter{\expandafter1\expandafter E%
        \the\numexpr\listlen\zerocount[1,2]-1\relax}%
    \else
      \expandafter\prefixmult\expandafter{\expandafter1\expandafter E%
        \expandafter-\the\numexpr\listlen\zerocount[2,1]\relax}%
    \fi
  \fi
}
\newcommand\prefixmult[1]{\prefixmultaux#1\relax}
\def\prefixmultaux 1E#1\relax{%
  \setsepchar{E4||E7||E10||E13||E16||E19||E22||E25||%
              E-5||E-8||E-11||E-14||E-17||E-20||E-23}%
  \readlist\factormult{E#1}%
  \ifnum\factormultlen>1\relax
    \prefixSN[10]{1E\the\numexpr#1-1\relax}
  \else
    \setsepchar{E5||E8||E11||E14||E17||E20||E23||E26||%
                E-4||E-7||E-10||E-13||E-16||E-19||E-22}%
    \readlist\factormult{E#1}%
    \ifnum\factormultlen>1\relax
      \prefixSN[100]{1E\the\numexpr#1-2\relax}
    \else
      \prefixSN[1]{1E#1}%
    \fi
  \fi
}
\begin{document}
\prefix{1E6}\par
\prefix{1E-15}\par
\prefix{1e-6}\par
\prefix{100}\par
\prefix{1000000}\par
\prefix{.001}\par
\prefix{.000001}\par
\prefix{1E5}\par
\prefix{10000}\par
\prefix{.0001}\par
\prefix{1E7}\par
\prefix{1E-5}\par
\prefix{1E8}\par
\prefix{1E-4}
\end{document}

在此处输入图片描述

补充

不确定这是否是一种改进,因为在我看来应该\prefix只处理 10 的幂。但是,如果有人希望有一个可以处理任何乘数的更通用的实现一位有效数字,那么它就是:

\documentclass{article}
\usepackage{siunitx,listofitems}
\newcommand\prefixSN[2][1]{#1\,\prefixSNaux#2\relax}
\def\prefixSNaux 1E#1\relax{%
  \ifnum#1=0\relax\else
  \ifnum#1=-1\relax\textrm{d}\else  
  \ifnum#1=-2\relax\textrm{c}\else  
  \ifnum#1=-3\relax\textrm{m}\else  
  \ifnum#1=-6\relax\textrm{\ensuremath{\mu}}\else  
  \ifnum#1=-9\relax\textrm{n}\else  
  \ifnum#1=-12\relax\textrm{p}\else  
  \ifnum#1=-15\relax\textrm{f}\else  
  \ifnum#1=-18\relax\textrm{a}\else  
  \ifnum#1=-21\relax\textrm{z}\else  
  \ifnum#1=-24\relax\textrm{y}\else  
  \ifnum#1=1\relax\textrm{da}\else  
  \ifnum#1=2\relax\textrm{h}\else  
  \ifnum#1=3\relax\textrm{k}\else  
  \ifnum#1=6\relax\textrm{M}\else  
  \ifnum#1=9\relax\textrm{G}\else  
  \ifnum#1=12\relax\textrm{T}\else  
  \ifnum#1=15\relax\textrm{P}\else  
  \ifnum#1=18\relax\textrm{E}\else  
  \ifnum#1=21\relax\textrm{Z}\else  
  \ifnum#1=24\relax\textrm{Y}\else 
  ??%
  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
}
\newcommand\prefix[1]{%
  \setsepchar{E||e}%
  \readlist\scinot{#1}%
  \ifnum\scinotlen>1\relax
    \itemtomacro\scinot[1]\premult%
    \itemtomacro\scinot[2]\temp%
    \expandafter\expandafter\expandafter\prefixmult
    \expandafter\expandafter\expandafter{%
    \expandafter\premult\expandafter E\temp}%
  \else
    \setsepchar{./1||2||3||4||5||6||7||8||9/0}%
    \readlist\zerocount{#1}%
    \ifnum\zerocountlen=1\relax
      \expandafter\expandafter\expandafter\def
        \expandafter\expandafter\expandafter\temp
        \expandafter\expandafter\expandafter{\zerocountsep[1,1]}%
      \expandafter\expandafter\expandafter\prefixmult
        \expandafter\expandafter\expandafter{%
        \expandafter\temp\expandafter E%
        \the\numexpr\listlen\zerocount[1,2]-1\relax}%
    \else
      \expandafter\expandafter\expandafter\def
        \expandafter\expandafter\expandafter\temp
        \expandafter\expandafter\expandafter{\zerocountsep[2,1]}%
      \expandafter\expandafter\expandafter\prefixmult
        \expandafter\expandafter\expandafter{%
        \expandafter\temp\expandafter E%
        \expandafter-\the\numexpr\listlen\zerocount[2,1]\relax}%
    \fi
  \fi
}
\newcommand\prefixmult[1]{\prefixmultaux#1\relax}
\def\prefixmultaux #1E#2\relax{%
  \setsepchar{E4||E7||E10||E13||E16||E19||E22||E25||%
              E-5||E-8||E-11||E-14||E-17||E-20||E-23}%
  \readlist\factormult{E#2}%
  \ifnum\factormultlen>1\relax
    \prefixSN[#10]{1E\the\numexpr#2-1\relax}
  \else
    \setsepchar{E5||E8||E11||E14||E17||E20||E23||E26||%
                E-4||E-7||E-10||E-13||E-16||E-19||E-22}%
    \readlist\factormult{E#2}%
    \ifnum\factormultlen>1\relax
      \prefixSN[#100]{1E\the\numexpr#2-2\relax}
    \else
      \prefixSN[#1]{1E#2}%
    \fi
  \fi
}
\begin{document}
\prefix{4E6}\par
\prefix{1E-15}\par
\prefix{1e-6}\par
\prefix{300}\par
\prefix{1000000}\par
\prefix{.001}\par
\prefix{.000004}\par
\prefix{1E5}\par
\prefix{10000}\par
\prefix{.0001}\par
\prefix{5E7}\par
\prefix{1E-5}\par
\prefix{3E8}\par
\prefix{1E-4}
\end{document}

在此处输入图片描述

答案3

这支持指数和标准符号。

不检查非零数字,输入被假定为以下形式

1<sequence of zeros>
-1<sequence of zeros>
1e<exponent>
1E<exponent>
-1e<exponent>
-1E<exponent>

范围是-1E-261E26。超出此范围的数字将默认失败。您可以添加有关范围的控制。

这是代码:请注意,大部分工作是由\__nebuch_prefix_exp:nn(确切地说是由:ee变体)完成的。

\documentclass{article}
\usepackage{siunitx} % also loads xparse and expl3

\ExplSyntaxOn
\NewDocumentCommand{\prefix}{m}
 {
  \nebuch_prefix:e { \tl_lower_case:n { #1 } }
 }

\seq_new:N \l__nebuch_prefix_input_seq

\cs_new_protected:Nn \nebuch_prefix:n
 {
  \seq_set_split:Nnn \l__nebuch_prefix_input_seq { e } { #1 }
  \int_compare:nTF { \seq_count:N \l__nebuch_prefix_input_seq > 1 }
   {% there was e/E
    \__nebuch_prefix_exp:ee
     { \seq_item:Nn \l__nebuch_prefix_input_seq { 1 } }
     { \seq_item:Nn \l__nebuch_prefix_input_seq { 2 } }
   }
   {% no e/E
    \__nebuch_prefix_long:n { #1 }
   }
 }
\cs_generate_variant:Nn \nebuch_prefix:n { e }

\cs_new_protected:Nn \__nebuch_prefix_exp:nn
 {
  \int_compare:nTF { \int_mod:nn { #2 } { 3 } < 0 }
   {
    \__nebuch_prefix_exp:ee
     { #1 \prg_replicate:nn { 3 + \int_mod:nn { #2 } { 3 } } { 0 } }
     { \int_eval:n { #2 - 3 - \int_mod:nn { #2 } { 3 } } }
   }
   {
    \SI
     {
      #1 % 1 or -1
      \prg_replicate:nn { \int_mod:nn { #2 } { 3 } } { 0 }
     }
     {
      \int_case:nn { \int_div_truncate:nn { #2 } { 3 } }
       {
        {-8}{\yocto\voidunit}
        {-7}{\zepto\voidunit}
        {-6}{\atto\voidunit}
        {-5}{\femto\voidunit}
        {-4}{\pico\voidunit}
        {-3}{\nano\voidunit}
        {-2}{\micro\voidunit}
        {-1}{\milli\voidunit}
        {0}{\!\voidunit}
        {1}{\kilo\voidunit}
        {2}{\mega\voidunit}
        {3}{\giga\voidunit}
        {4}{\tera\voidunit}
        {5}{\peta\voidunit}
        {6}{\exa\voidunit}
        {7}{\zetta\voidunit}
        {8}{\yotta\voidunit}
       }
     }
   }
 }
\cs_generate_variant:Nn \__nebuch_prefix_exp:nn {ee}

\cs_new_protected:Nn \__nebuch_prefix_long:n
 {
  % count the number of zeros
  \regex_split:nnN { (\-*1+) } { #1 } \l__nebuch_prefix_input_seq
  \__nebuch_prefix_exp:ee
   { \seq_item:Nn \l__nebuch_prefix_input_seq { 2 } }
   { \tl_count:e { \seq_item:Nn \l__nebuch_prefix_input_seq { 3 } } }
 }
\cs_generate_variant:Nn \tl_count:n { e }
\ExplSyntaxOff

\DeclareSIUnit{\voidunit}{\relax}

\begin{document}

\prefix{1e0}

\prefix{1e2}

\prefix{1E4}

\prefix{-1E20}

\prefix{1}

\prefix{10}

\prefix{100}

\prefix{1000}

\prefix{10000}

\prefix{100000}

\prefix{1000000}

\prefix{10000000}

\prefix{100000000}

\prefix{1000000000}

\prefix{-10000000000}

\prefix{100000000000}

\prefix{1E6}

\prefix{1E24}

\prefix{-1E-6}

\prefix{1E-8}

\prefix{-1E-7}

\end{document}

在此处输入图片描述

答案4

使用新版本的 siunitx,现在可以执行以下操作:

    \DeclareSIUnit\noop{\relax}

    \qty[drop-zero-decimal
         round-precision = 3, 
         scientific-notation = engineering,
         exponent-to-prefix
        ]{1000000}{\noop}

相关内容