TransWikia.com

Simple implementation of the abs function by getting rid of or by consuming the "-"?

TeX - LaTeX Asked on June 9, 2021

I wish to build a (simple) implementation of abs.
I tried FPabs but I get a lot of zeros after the decimal point. I could get rid of them with the FP round function, but I don’t know the number of decimals I will have.

I think a string manipulation approach is better. I could use the xstring package to get rid of the minus as in -12.3 to get 12.3 but a full package just for that …

Is there a simple way to get rid or consume the "-" of "-12.3" for instance to get "12.3" ?

4 Answers

You can define gobbleminus by this way:

defgobbleminus#1{ifx-#1else#1fi}

%test:
gobbleminus -12

gobbleminus 1234

gobbleminus .24

Correct answer by wipet on June 9, 2021

I'm sure you get a LaTeX3 solution by one of the experts in no time and I really don't think that a string-based solution is the best you can do here, but while you wait here is a simple, argument-based solution.

The solution is not expandable, which might be a bit of deal-breaker depending on what you want to do (but: see below).

The main idea is to use delimited arguments (How does TeX look for delimited arguments?) to strip characters from a string. In order not to run into trouble here, we first check whether the characters are actually present.

ifstrstartswith works by defining a helper macro fstr@ifstrstartswith@i that grabs arguments as follows

#1<characters to find>#2&

if we now pass a string to fstr@ifstrstartswith@i that starts with <characters to find>, TeX's argument parse rules make #1 come up empty. In order to avoid errors, we always call fstr@ifstrstartswith@i with fstr@ifstrstartswith@i <string><characters to find>&. This means that even if <string> does not contain <characters to find> at all, we still have called the macro with the right argument signature.

The characters stripping now works similarly. We check that <string> actually starts with <characters to remove> and then apply a helper macro fstr@stripfromstart@i with signature

<characters to remove>#1&

which then strips off the <characters to remove> from the beginning of <string> when called as fstr@stripfromstart@i <string>&.

documentclass[british]{article}
usepackage[T1]{fontenc}
usepackage{babel}

usepackage{etoolbox}

makeatletter
% {<characters to find>}{<string>}
newrobustcmd*{ifstrstartswith}[2]{%
  deffstr@ifstrstartswith@i##1#1##2&{%
    ifblank{##1}}%
  fstr@ifstrstartswith@i#2#1&}

% {<characters to remove>}{<string>}
newrobustcmd*{stripfromstart}[2]{%
  deffstr@stripfromstart@i#1##1&{##1}%
  ifstrstartswith{#1}{#2}
    {fstr@stripfromstart@i#2&}
    {#2}}
makeatother

newcommand*{mysimpleabs}{stripfromstart{-}}

begin{document}
mysimpleabs{4.5}

mysimpleabs{-4.5}
end{document}

4.5//4.5

Of course you don't want to try mysimpleabs{-4.5-5} or mysimpleabs{-4.5+5}.


edit Come to think of it, we can make this expandable if we hard-code the -. Still, string manipulation does not strike me as the best way to deal with this.

documentclass[british]{article}
usepackage[T1]{fontenc}
usepackage{babel}

usepackage{etoolbox}

makeatletter
newcommand*{fstr@ifstrstartswithminus@i}{} % just to reserve the name
deffstr@ifstrstartswithminus@i#1-#2&{ifblank{#1}}

newcommand*{ifstrstartswithminus}[1]{%
  fstr@ifstrstartswithminus@i#1-&}

newcommand*{fstr@stripminusfromstart@i}{} % just to reserve the name
deffstr@stripminusfromstart@i-#1&{#1}

newcommand*{stripminusfromstart}[1]{%
  ifstrstartswithminus{#1}
    {fstr@stripminusfromstart@i#1&}
    {#1}}
makeatother

newcommand*{mysimpleabs}{stripminusfromstart}

begin{document}
mysimpleabs{4.5}

mysimpleabs{-4.5}

edeffoo{mysimpleabs{12.3}}
meaningfoo

edeffoo{mysimpleabs{-12.3}}
meaningfoo
end{document}

Answered by moewe on June 9, 2021

You could use xfp (the package is very short, it contains only two definitions to create wrappers around fp_eval:n and int_eval:n):

documentclass{article}
usepackage{xfp}
begin{document}

fpeval{abs(-12.3)}

end{document}

enter image description here

It is expandable, that means you can fed it to num for formatting. Here I change the period to a command:

documentclass{article}
usepackage{xfp,siunitx}

sisetup{locale=DE}
begin{document}

num{fpeval{abs(-12.3)}}

end{document}

enter image description here

Answered by Ulrike Fischer on June 9, 2021

If you know that the thing you're removing the minus from starts with a minus or a digit, then the old school way of doing this would be

makeatletter
defgobbleminus#1{ifnum#17<z@ @firstoftwofi #1}
makeatother

e.g.

gobbleminus 12.3    % Expands to 12.3
gobbleminus -12.3   % Expands to 12.3
gobbleminus -0.1000 % Expands to 0.1000

If your data is being generated by expanding something, you will of course need to make sure it gets sufficiently expanded before letting gobbleminus act on it.

Another catch is that

gobbleminus .123    % Raises error

since . is not a minus or digit. Though it occurs to me that one could make it tolerate this as well, by changing the definition to

makeatletter
defgobbleminus#1{ifdim#17pt<z@ @firstoftwofi #1}
makeatother

Answered by Lars H on June 9, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP