Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

As I am now accessing values from tables via a macro to make dynamic references in the text (see here) and make calculations with it, I need a way to make the resulting variables under which the values are stored more user friendly. Because of LaTeX' limitation of not allowing numbers or any separator in a macro name (and I have many macros), I use the "\csname\dots\endcsname" approach from the TeX FAQ to create more meaningful names.

This results in the following complication, illustrated in the MWE below: I use siunitx to print numbers via \num or \SI. As these commands accept only numbers, I can't make calculations within those. I use l3fp to make the calculations and store the the result under a (temporary) name. This results in two lines of code for every single variable:

\calc{\Numtest}{500/2}% store result
\DefineRemark{num:test}{\Numtest}% label result

And within the text the result would be called as \num{\Remark{num:test}}. I want to combine the two commands into one that would be something like \newcalc{<varname>}{<calculation>}. The following does not work because \temp does not get overwritten. I would require a new random name for every single instance of \newcalc.

\newcommand{\newcalc}[2]{%
\calc{\temp}{#2}
\DefineRemark{#1}{\temp}
}

What is a way to solve this issue? On another note in the MWE I created two new commands based on \num and \SI that incorporates the \Remark macro. Is it a good idea to create these commands by copying the original definition completely?

\documentclass{scrartcl}

\usepackage{siunitx,xparse,expl3}

% simple calculation command that stores the result
\ExplSyntaxOn
\NewDocumentCommand {\calc} { m m } {
  \tl_set:Nx #1 { \fp_to_tl:n {#2} }
}
\ExplSyntaxOff

% Workaround for non-letters in macro names
\newcommand{\DefineRemark}[2]{%
  \expandafter\newcommand\csname rmk-#1\endcsname{#2}%
}
\newcommand{\Remark}[1]{\csname rmk-#1\endcsname}

% Create \Num and \NUM based on siunitx' \num and \SI to incorporate the above directly
% (so \Num is \num{\remark{.}}). Not sure if it as a good idea to do it that way
\ExplSyntaxOn
\NewDocumentCommand \Num { o m } {
  \leavevmode
  \group_begin:
    \IfNoValueF {#1}
      { \keys_set:nn { siunitx } {#1} }
    \__siunitx_number_output:n {\Remark{#2}}
  \group_end:
}
\NewDocumentCommand \NUM { o m o m } {
  \leavevmode
  \group_begin:
    \IfNoValueTF {#1}
      { \__siunitx_combined:nnnn { } {\Remark{#2}} {#3} {#4} }
      {
        \keys_set:nn { siunitx } {#1}
        \__siunitx_combined:nnnn {#1} {\Remark{#2}} {#3} {#4}
      }
  \group_end:
}
\ExplSyntaxOff

% Test if \Num and \NUM Works
\calc{\Numtest}{500/2}
\DefineRemark{num:test}{\Numtest}

% Trying to combine \calc and \DefineRemark
% This does not work :( Would need to create a new "\temp" each time
\newcommand{\newcalc}[2]{%
\calc{\temp}{#2}
\DefineRemark{#1}{\temp}
}

\newcalc{new:calc1}{250/2}
\newcalc{new:calc2}{125/2}


\begin{document}

Test Num and NUM Macros: \Num{num:test} and \NUM{num:test}{\percent}

\vspace{2ex}

Test the combination of calc and DefineRemark. The two values should be
different: \Num{new:calc1} and \Num{new:calc2}

\end{document}
share|improve this question
    
It sounds like fp.sty would do what you want.. \FPdiv\myresult{250}{2} will store 125 in \myresult. \FPclip is also necessary to remove leading and trailing zeros. But this approach integrates well with siunitx –  Brandon Kuczenski Dec 1 '12 at 21:52
1  
As l3fp is expandable, why not just do the calculation within the argument of \num? –  Joseph Wright Dec 1 '12 at 21:55
    
@BrandonKuczenski The problem would be the same, since his \calc{\myresult}{250 / 2} is equivalent to \FPdiv\myresult{250}{2} (up to how leading/trailing zeros are treated, perhaps). –  Bruno Le Floch Dec 2 '12 at 9:49

1 Answer 1

up vote 7 down vote accepted

I suggest quite a different approach:

\documentclass{scrartcl}

\usepackage{siunitx,xparse,expl3}

% simple calculation command that stores the result
\ExplSyntaxOn
\NewDocumentCommand{\calc}{ m m }
 {
  \tl_set:cx { l_jorg_rmk_#1_tl } { \fp_to_tl:n { #2 } }
 }
\NewDocumentCommand{\usekey}{ m }
 {
  \tl_use:c { l_jorg_rmk_#1_tl }
 }
% Create \Num and \NUM based on siunitx' \num and \SI to incorporate the above directly
% (so \Num is \num{\remark{.}}). Not sure if it as a good idea to do it that way
\NewDocumentCommand \Num { o m }
 {
  \leavevmode
  \group_begin:
    \IfNoValueF {#1}
      { \keys_set:nn { siunitx } {#1} }
    \__siunitx_number_output:n {\tl_use:c { l_jorg_rmk_#2_tl } }
  \group_end:
}
\NewDocumentCommand \NUM { o m o m }
 {
  \leavevmode
  \group_begin:
    \IfNoValueTF {#1}
      { \__siunitx_combined:nnnn { } { \tl_use:c { l_jorg_rmk_#2_tl } } {#3} {#4} }
      {
        \keys_set:nn { siunitx } {#1}
        \__siunitx_combined:nnnn {#1} { \tl_use:c { l_jorg_rmk_#2_tl } } {#3} {#4}
      }
  \group_end:
}
\ExplSyntaxOff

% Test if \Num and \NUM Works
\calc{num:test}{500/2}
\calc{new:calc1}{250/2}
\calc{new:calc2}{125/2}


\begin{document}

Test Num and NUM Macros: \Num{num:test} and \NUM{num:test}{\percent}

\vspace{2ex}

Test the combination of calc and DefineRemark. The two values should be
different: \Num{new:calc1} and \Num{new:calc2}

\vspace{2ex}

Here's a key used by itself: \usekey{num:test}

\end{document}

However, using \__siunitx_number_output:n and \__siunitx_combined:nnnn is an improper way to proceed, because these are commands prefixed by __ which makes them "internals", so bound to be changed without notice (thanks Bruno Le Floch for remarking it).

In my opinion siunitx should make available public interfaces to the internal commands, but at the moment there are many risks in using the commands in this way. So a better suggestion is to change the definitions of \Num and \NUM as follows

\NewDocumentCommand{\Num} { o m }
 {
   \num [ #1 ] { \tl_use:c { l_jorg_rmk_#2_tl } }
 }

\NewDocumentCommand{\NUM} { o m o m }
 {
  \SI [ #1 ] { \tl_use:c { l_jorg_rmk_#2_tl } } [ #3 ] { #4 }
 }

which is even simpler. The calls of \SI and \num should take into account by themselves the \IfNoValue... business.

share|improve this answer
    
That's brilliant so much simpler than what I was trying to do. It's alsway great that I can just use the integer via usekey. Thanks! –  Jörg Dec 2 '12 at 11:44
    
@egreg: if you change the definitions of \Num and \NUM to what I give in my answer (without using the internals of siunitx, likely to change), I can delete my answer so that there is one definite answer. Perhaps it is worth to explicitly point out that internals should not be used. –  Bruno Le Floch Dec 2 '12 at 13:24
    
@BrunoLeFloch You're right and Joseph is wrong in not providing a "non internal" macro, but defining \SI and \num in terms of internals only. We were discussing exactly this in chat a couple of days ago. –  egreg Dec 2 '12 at 13:40
    
@BrunoLeFloch The reason why I copied the commands from siunitx was because I wanted to alter my custom command to \Num[SI-options]<pre-unit>{number}[measure] where all but number are optional. I have done this by creating a \cs_new_protected:Npn that changes the order of the original SI command and then define the \Num command accordingly. I don't know if this can be done in an easier way without relying on siunitx internals. –  Jörg Dec 2 '12 at 14:41
    
@Jörg I encourage you to write a question about that: your comment is not detailed enough for me to understand exactly what you want to acheive. It should be possible perhaps to do \NewDocumentCommand{\Num}{omo}{\IfNoValue{#3}{\num[#1]{#2}}{\SI[#1]{#2}{#3}}}? –  Bruno Le Floch Dec 5 '12 at 13:13

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.