Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

\ifnum for real numbers

1,107 views
Skip to first unread message

Pluto

unread,
Oct 14, 2010, 1:44:43 PM10/14/10
to
TeX's \ifnum accepts integers only. Is there a corresponding test for
real numbers, eg \ifnum 0.5<1\fi?

zappathustra

unread,
Oct 14, 2010, 1:50:08 PM10/14/10
to
Le 14/10/2010 19:44, Pluto a �crit :

> TeX's \ifnum accepts integers only. Is there a corresponding test for
> real numbers, eg \ifnum 0.5<1\fi?
No, but you can try:

\long\def\first#1#2{#1}
\long\def\second#1#2{#2}
\def\Ifnum#1#2#3{%
\ifdim#1pt#2#3pt
\first
\else
\second
\fi
}

\def\Ifnum{0.5}<{1.2}{Perfect.}{Too bad.}

Paul

zappathustra

unread,
Oct 14, 2010, 1:52:50 PM10/14/10
to
Le 14/10/2010 19:50, zappathustra a �crit :

> \def\Ifnum{0.5}<{1.2}{Perfect.}{Too bad.}

Oops, remove the \def here.

Paul

zappathustra

unread,
Oct 14, 2010, 1:54:37 PM10/14/10
to
Le 14/10/2010 19:50, zappathustra a �crit :
> \def\Ifnum#1#2#3{%
> \ifdim#1pt#2#3pt
> \first
> \else
> \second
> \fi
> }

Damn, I can't do something properly tonight. Add \expandafter before
\first and \second:


\def\Ifnum#1#2#3{%
\ifdim#1pt#2#3pt
\expandafter\first
\else
\expandafter\second
\fi
}

Paul

Joseph Wright

unread,
Oct 14, 2010, 2:25:28 PM10/14/10
to
This is of course limited to numbers that are within the range for TeX
dimensions.
--
Joseph Wright

GL

unread,
Oct 14, 2010, 2:42:11 PM10/14/10
to
Le 14/10/2010 19:44, Pluto a �crit :
> TeX's \ifnum accepts integers only. Is there a corresponding test for
> real numbers, eg \ifnum 0.5<1\fi?

\ifnum\number\dimexpr.5pt<\number\dimexpr1pt\relax
with eTeX... of course

Pluto

unread,
Oct 15, 2010, 2:18:22 PM10/15/10
to
On Oct 14, 7:42 pm, GL <gouail...@gmail.com> wrote:

> Le 14/10/2010 19:44, Pluto a écrit :
>
> > TeX's \ifnum accepts integers only. Is there a corresponding test for
> > real numbers, eg \ifnum 0.5<1\fi?
>
> \ifnum\number\dimexpr.5pt<\number\dimexpr1pt\relax
>         with eTeX... of course

I think Paul's approach of using \ifdim may be preferable to \ifnum
with \number and \dimexpr. I have noticed that two \relax's may be
necessary in the latter approach, because \ifnum and \number both
expand tokens until ...

Paul's approach requires braces at invocation time, for proper
scoping. Consider the following, taken from 'ltxtools':

\long\def\ifnnum#1\fi{%
\begingroup
\def\rsvda##1{%
\long\def\rsvda####1####2{%
\long\edef\rsvdb{{\string:}{\detokenize{####2}}}%
\expandafter\ifmemberTF\rsvdb{%
\long\def\rsvdb########1:########2\@nil{%
%% Test for TeX's dimension limit here.
\passexpanded{%
\endgroup\unexpanded{\ifdim####1pt}\rsvdc
\unexpanded{########1pt\relax########2\else}%
\the\toks@\noexpand\fi
}%
}%
\rsvdb####2\@nil
}{%
\endgroup
\@latex@error{I can't find '\string:' in the argument of
\string\ifnnum}\@ehc
}%
}%
\ifmemberTF{=}{##1}{%
\long\def\rsvdb####1=####2\@nil{%
\def\rsvdc{=}%
\rsvda{####1}{####2}%
}%
\rsvdb##1\@nil
}{%
\ifmemberTF{>}{##1}{%
\long\def\rsvdb####1>####2\@nil{%
\def\rsvdc{>}%
\rsvda{####1}{####2}%
}%
\rsvdb##1\@nil
}{%
\ifmemberTF{<}{##1}{%
\long\def\rsvdb####1<####2\@nil{%
\def\rsvdc{<}%
\rsvda{####1}{####2}%
}%
\rsvdb##1\@nil
}{%
\@latex@error{Wrong argument for \string\ifnnum}\@ehc
}%
}%
}%
}%
\long\edef\rsvdb{{\string\else}{\detokenize{#1}}}%
\expandafter\ifmemberTF\rsvdb{%
\long\def\rsvdb##1\else##2\@nil{%
\toks@{##2}\rsvda{##1}%
}%
\rsvdb#1\@nil
}{%
\toks@{}\rsvda{#1}%
}%
}


\ifnnum.5=.1:\def\x#1{x#1x}\else\def\y#1{y#1y}\fi

\ifnnum.5<.1:\def\x#1{x#1x}\fi

\ifnnum.5>.1:\else\def\x#1{x#1x}\fi

zappathustra

unread,
Oct 15, 2010, 2:40:20 PM10/15/10
to
Le 15/10/2010 20:18, Pluto a �crit :

> On Oct 14, 7:42 pm, GL<gouail...@gmail.com> wrote:
>> Le 14/10/2010 19:44, Pluto a �crit :

Ouuuh, tasty code... But it leads to trouble:

\iffalse
\ifnnum .5=.1: whatever \else anything\fi
\fi

It produces an extra \fi, because when TeX skips a conditional it maches
\if's and \else's and \fi's, but \ifnnum isn't a real \if, it's a macro,
and TeX thinks the \else and \fi that follow belong to \iffalse. Then
the last \fi is seen as extra.

When devising conditionals that are really macros, you can't use \else
and \fi.

Paul

Pluto

unread,
Oct 15, 2010, 3:01:49 PM10/15/10
to
On Oct 15, 7:40 pm, zappathustra <zappathus...@free.fr> wrote:

>   Le 15/10/2010 20:18, Pluto a écrit :
>
>
>
>
>
> > On Oct 14, 7:42 pm, GL<gouail...@gmail.com>  wrote:
> Paul- Hide quoted text -
>
> - Show quoted text -

Thanks for that trap. What of

\iffalse
\ifnnum .5=.1: whatever \xelse anything\xfi
\fi

where both \xelse and \xfi are defined/hidden in \ifnnum?

zappathustra

unread,
Oct 15, 2010, 3:11:30 PM10/15/10
to
Le 15/10/2010 21:01, Pluto a �crit :

> On Oct 15, 7:40 pm, zappathustra<zappathus...@free.fr> wrote:
>> Le 15/10/2010 20:18, Pluto a �crit :

>>
>>
>>
>>
>>
>>> On Oct 14, 7:42 pm, GL<gouail...@gmail.com> wrote:
>>>> Le 14/10/2010 19:44, Pluto a �crit :

Yes, that one is perfectly ok, as long \xelse and \xfi aren't \let to
\else and \fi, which I suppose isn't the case.

Paul

Pluto

unread,
Oct 15, 2010, 3:12:05 PM10/15/10
to
> where both \xelse and \xfi are defined/hidden in \ifnnum?- Hide quoted text -

>
> - Show quoted text -

In fact, both \xelse and \xfi don't have to be defined: they are
merely delimiters.

zappathustra

unread,
Oct 15, 2010, 3:15:45 PM10/15/10
to
Le 15/10/2010 21:12, Pluto a �crit :

> On Oct 15, 8:01 pm, Pluto<a.m...@rocketmail.com> wrote:
>> On Oct 15, 7:40 pm, zappathustra<zappathus...@free.fr> wrote:
>>
>>
>>
>>
>>
>>> Le 15/10/2010 20:18, Pluto a �crit :

>>>> On Oct 14, 7:42 pm, GL<gouail...@gmail.com> wrote:
>>>>> Le 14/10/2010 19:44, Pluto a �crit :

If you're going to do complex code, defining them can have its use,
though, at least at development time:

\def\xelse{\errmessage{This \string\xelse shouldn't happen}}
\def\xfi{\errmessage{This \string\xfi shouldn't happen}}

That's just a bit more informative than ``undefined control sequence''.

Paul

Donald Arseneau

unread,
Oct 15, 2010, 4:44:50 PM10/15/10
to
Pluto <a.m...@rocketmail.com> writes:

> Paul's approach requires braces at invocation time, for proper
> scoping. Consider the following, taken from 'ltxtools':
>
> \long\def\ifnnum#1\fi{%
> \begingroup
> \def\rsvda##1{%
> \long\def\rsvda####1####2{%
> \long\edef\rsvdb{{\string:}{\detokenize{####2}}}%

[much more hackery]


>
> \ifnnum.5=.1:\def\x#1{x#1x}\else\def\y#1{y#1y}\fi

If you need a delimiter (:) anyway, I suggest \then as in

\let\then\iffalse

Then you can use regular \else and \fi and normal nesting

\def\ifnnum#1\then{...}


--
Donald Arseneau as...@triumf.ca

zappathustra

unread,
Oct 15, 2010, 5:40:31 PM10/15/10
to
Le 15/10/2010 22:44, Donald Arseneau a �crit :

I'm always impressed by this kind of extremely simple wizardry.

Then, Pluto, I don't know what the internal code in your macro is
supposed to do (I haven't tried to understand it), but you might find
the following one somewhat easier to maintain. And it is fully
expandable, like a real \if... (except you have to remove the error
message).

\def\first#1#2{#1}
\def\second#1#2{#2}
\let\anemptycommand\relax
\def\checkequal#1=#2\end{%
\ifcsname anempty#2command\endcsname
\expandafter\second
\else
\expandafter\first
\fi
}
\def\checksmaller#1<#2\end{%
\ifcsname anempty#2command\endcsname
\expandafter\second
\else
\expandafter\first
\fi
}
\def\checklarger#1>#2\end{%
\ifcsname anempty#2command\endcsname
\expandafter\second
\else
\expandafter\first
\fi
}

\def\ifequal#1=#2\end{%
\ifdim#1pt=#2pt%
}
\def\ifsmaller#1<#2\end{%
\ifdim#1pt<#2pt%
}
\def\iflarger#1>#2\end{%
\ifdim#1pt>#2pt%
}

\let\then\iffalse

\def\ifnnum#1\then{%
\checkequal#1=\end
{\ifequal#1\end}
{\checksmaller#1<\end
{\ifsmaller#1\end}
{\checklarger#1>\end
{\iflarger#1\end}
{\errmessage{The expression #1 isn't properly formed}%
\iffalse}}}%
}


\edef\test{%
\iffalse\else
\ifnnum1.45>1.448\then Yes!\else No!\fi
\fi
}
\meaning\test


Best,
Paul

GL

unread,
Oct 15, 2010, 6:06:18 PM10/15/10
to
Le 15/10/2010 20:18, Pluto a �crit :
> On Oct 14, 7:42 pm, GL<gouail...@gmail.com> wrote:
>> Le 14/10/2010 19:44, Pluto a �crit :

>>
>>> TeX's \ifnum accepts integers only. Is there a corresponding test for
>>> real numbers, eg \ifnum 0.5<1\fi?
>>
>> \ifnum\number\dimexpr.5pt<\number\dimexpr1pt\relax
>> with eTeX... of course
>
> I think Paul's approach of using \ifdim may be preferable to \ifnum
> with \number and \dimexpr. I have noticed that two \relax's may be
> necessary in the latter approach, because \ifnum and \number both
> expand tokens until ...


Here \ifnum\number\dimexpr is stopped by <
and \number\dimexpr1pt by \relax

It's just simple and purely expandable. \number\dimexpr converts
any units in internal scaled-points (without leading `sp').
I think this worth to be known...

Philipp Stephani

unread,
Oct 15, 2010, 6:31:16 PM10/15/10
to
GL <goua...@gmail.com> writes:

> Le 15/10/2010 20:18, Pluto a écrit :
>> On Oct 14, 7:42 pm, GL<gouail...@gmail.com> wrote:

>>> Le 14/10/2010 19:44, Pluto a écrit :
>>>
>>>> TeX's \ifnum accepts integers only. Is there a corresponding test for
>>>> real numbers, eg \ifnum 0.5<1\fi?
>>>
>>> \ifnum\number\dimexpr.5pt<\number\dimexpr1pt\relax
>>> with eTeX... of course
>>
>> I think Paul's approach of using \ifdim may be preferable to \ifnum
>> with \number and \dimexpr. I have noticed that two \relax's may be
>> necessary in the latter approach, because \ifnum and \number both
>> expand tokens until ...
>
>
> Here \ifnum\number\dimexpr is stopped by <
> and \number\dimexpr1pt by \relax
>
> It's just simple and purely expandable.

Really? Try this:

\def\test{%
\ifnum\number\dimexpr2pt<\number\dimexpr1pt\relax
1%
\else
0%
\fi
}
\test % should print "0"
\edef\x{\test}
\tt\meaning\x % should print "macro->0"
\bye

--
Change “LookInSig” to “tcalveu” to answer by mail.

zappathustra

unread,
Oct 15, 2010, 6:49:55 PM10/15/10
to

Yes, you need the double \relax, one to end \dimexpr, another one to end
\number, otherwise ends evaluating the second number when bumping into
\else (enrolling the 1), and inserts a \relax. Why? Because it's like that.

Paul

Enrico Gregorio

unread,
Oct 15, 2010, 7:15:40 PM10/15/10
to
zappathustra <zappat...@free.fr> wrote:

> Le 16/10/2010 00:31, Philipp Stephani a ÔøΩcrit :
> > GL<goua...@gmail.com> writes:
> >
> >> Le 15/10/2010 20:18, Pluto a ÔøΩcrit :


> >>> On Oct 14, 7:42 pm, GL<gouail...@gmail.com> wrote:

> >>>> Le 14/10/2010 19:44, Pluto a ÔøΩcrit :

\ifnum is happy if it is passed a dimension for testing.
So \number is not necessary.

\def\test{%
\ifnum\dimexpr2pt<\dimexpr1pt\relax


1%
\else
0%
\fi
}
\test % should print "0"
\edef\x{\test}
\tt\meaning\x % should print "macro->0"
\bye

Ciao
Enrico

Donald Arseneau

unread,
Oct 15, 2010, 11:20:12 PM10/15/10
to
zappathustra <zappat...@free.fr> writes:

> \def\ifnnum#1\then{%
> \checkequal#1=\end
> {\ifequal#1\end}
> {\checksmaller#1<\end
> {\ifsmaller#1\end}
> {\checklarger#1>\end
> {\iflarger#1\end}
> {\errmessage{The expression #1 isn't properly formed}%
> \iffalse}}}%
> }

Here's the short one I came up with:


\let\then\iffalse
\def\gobblejunk#1\delimiter{}

\def\ifnnum#1\then{\ifdim
\ptlt\ptgt\pteq #1pt\gobblejunk<=>\delimiter
}

\def\ptlt#1<{#1pt<}
\def\ptgt#1>{#1pt>}
\def\pteq#1={#1pt=}

--
Donald Arseneau as...@triumf.ca

GL

unread,
Oct 16, 2010, 4:13:42 AM10/16/10
to
Le 16/10/2010 01:15, Enrico Gregorio a ÔøΩcrit :

> zappathustra<zappat...@free.fr> wrote:
>
>> Le 16/10/2010 00:31, Philipp Stephani a ÔøΩcrit :
>>> GL<goua...@gmail.com> writes:
>
> \ifnum is happy if it is passed a dimension for testing.
> So \number is not necessary.

Well thanks, I didn't know that..

zappathustra

unread,
Oct 16, 2010, 4:20:13 AM10/16/10
to
Le 16/10/2010 05:20, Donald Arseneau a �crit :

I'm very impressed again, and terribly so...
That makes me feel as if there were chaos in the heart of TeX (because
you can come up with apparently unpredictable solutions).
/Expansion/, how weird a word is that?

[I shouldn't be writing here before coffee is fully ingested. Makes me
lyrical. As in trash metal lyrics.]

Paul

GL

unread,
Oct 16, 2010, 4:33:06 AM10/16/10
to
Le 16/10/2010 05:20, Donald Arseneau a �crit :
> zappathustra<zappat...@free.fr> writes:
>
> Here's the short one I came up with:
>
>
> \let\then\iffalse
> \def\gobblejunk#1\delimiter{}
>
> \def\ifnnum#1\then{\ifdim
> \ptlt\ptgt\pteq #1pt\gobblejunk<=>\delimiter
> }
>
> \def\ptlt#1<{#1pt<}
> \def\ptgt#1>{#1pt>}
> \def\pteq#1={#1pt=}
>
Very nice ;-) Thanks.

If you want \unless to work with, a modification's required:

%\let\then\iffalse
\def\gobblejunk#1\delimiter{}

\def\realnums#1\then{\dimexpr


\ptlt\ptgt\pteq #1pt\gobblejunk<=>\delimiter
}

\def\ptlt#1<{#1pt<\dimexpr}
\def\ptgt#1>{#1pt>\dimexpr}
\def\pteq#1={#1pt=\dimexpr}

\unless\ifnum\realnums3.45<4.25\then 0\else 1\fi

I find weird that \unless doesn't expand the following macro...

Dan

unread,
Oct 17, 2010, 6:26:42 PM10/17/10
to
On Oct 15, 6:15 pm, Enrico Gregorio <grego...@math.unipd.it> wrote:
> zappathustra <zappathus...@free.fr> wrote:
> >   Le 16/10/2010 00:31, Philipp Stephani a Žcrit :

>
> > > \def\test{%
> > >    \ifnum\number\dimexpr2pt<\number\dimexpr1pt\relax
> > >      1%
> > >    \else
> > >      0%
> > >    \fi
> > > }
> > > \test         % should print "0"
> > > \edef\x{\test}
> > > \tt\meaning\x % should print "macro->0"
> > > \bye
>
> > Yes, you need the double \relax, one to end \dimexpr, another one to end
> > \number, otherwise ends evaluating the second number when bumping into
> > \else (enrolling the 1), and inserts a \relax. Why? Because it's like that.
>
> \ifnum is happy if it is passed a dimension for testing.
> So \number is not necessary.

To be absolutely clear: it is happy with some methods of expressing
a dimension: a register, the results of \dimexpr and others (for
example,
\fontdimen 2\font), but it certainlycomplains about explicitly written
dimensions like 2pt.

>
> \def\test{%
>   \ifnum\dimexpr2pt<\dimexpr1pt\relax
>     1%
>   \else
>     0%
>   \fi}
>
> \test         % should print "0"
> \edef\x{\test}
> \tt\meaning\x % should print "macro->0"
> \bye
>
> Ciao
> Enrico


Dan

Pluto

unread,
Oct 18, 2010, 5:30:33 PM10/18/10
to
On Oct 15, 10:40 pm, zappathustra <zappathus...@free.fr> wrote:
> Paul- Hide quoted text -
>
> - Show quoted text -

It is always good to find expandable commands, although they seem to
always require a tonne of auxiliary commands which must always be
defined.

GL

unread,
Oct 19, 2010, 4:16:54 AM10/19/10
to
Le 18/10/2010 23:30, Pluto a �crit :

> On Oct 15, 10:40 pm, zappathustra<zappathus...@free.fr> wrote:
>> Le 15/10/2010 22:44, Donald Arseneau a �crit :

>>
>
> It is always good to find expandable commands, although they seem to
> always require a tonne of auxiliary commands which must always be
> defined.

That's depend. Is this a "tonne" or a "ton" ? or may be a town ?

Heiko Oberdiek

unread,
Oct 19, 2010, 12:33:43 PM10/19/10
to
GL <goua...@gmail.com> wrote:

\ifdim\dimexpr.5pt<\dimexpr1pt\relax

--
Heiko Oberdiek

Pluto

unread,
Oct 20, 2010, 9:19:46 AM10/20/10
to
On Oct 16, 4:20 am, Donald Arseneau <a...@triumf.ca> wrote:
> Donald Arseneau                          a...@triumf.ca

1. Donald will always manage to produce a simple and compact solution
from his pocket.

\@nilgobble is already defined in ltxtools; so I use it below:

\def\ifnnum#1\then{\ifdim\@ptlt\@ptgt\@pteq #1pt\@nilgobble<=>
\LTS@nil}
\def\@ptlt#1<{#1pt<}
\def\@ptgt#1>{#1pt>}
\def\@pteq#1={#1pt=}


2. I can see why GL insists on a solution using \dimexpr. If you do

\edef\test{\number\dimexpr 4.45pt}

you get 291635. Where has the unit (pt) gone? Was it removed by
\number? Does \number do any rounding up or down? I couldn't find the
answer in the TeXBook and the eTeX manual.

3. If we remove the error message from Paul's solution to ensure
expandability, we can't catch errors, unlike in Donald's solution.
Paul's solution is as follows. So besides compactness, Donald's
solution has one additional advantage: \ifdim will complain of
something wrong.

\def\ifnnum@aux#1{\@nameuse{LTS@\ifcsnameTF{nu#1ll}21of2}}
\def\LTS@checkequal#1=#2\@nil{\ifnnum@aux{#2}}
\def\LTS@checklthan#1<#2\@nil{\ifnnum@aux{#2}}
\def\LTS@checkgthan#1>#2\@nil{\ifnnum@aux{#2}}
\def\LTS@ifequal#1=#2\@nil{\ifdim#1pt=#2pt}
\def\LTS@iflthan#1<#2\@nil{\ifdim#1pt<#2pt}
\def\LTS@ifgthan#1>#2\@nil{\ifdim#1pt>#2pt}
\let\then\iffalse
\def\xifnnum#1\then{%
\LTS@checkequal#1=\@nil
{\LTS@ifequal#1\@nil}
{\LTS@checklthan#1<\@nil
{\LTS@iflthan#1\@nil}
{\LTS@checkgthan#1>\@nil
{\LTS@ifgthan#1\@nil}
{\iffalse}}}% error message removed
}

Try

\ifnnum 2.5\then\endgraf\fi
\xifnnum 2.5\then\endgraf\fi

GL

unread,
Oct 20, 2010, 9:34:28 AM10/20/10
to
Le 20/10/2010 15:19, Pluto a �crit :

> On Oct 16, 4:20 am, Donald Arseneau<a...@triumf.ca> wrote:

> \@nilgobble is already defined in ltxtools; so I use it below:

And best, (because your macros are not \long) :
\remove@to@nnil is already defined in latex.ltx
(see definition of \@defaultunits)

Message has been deleted

Enrico Gregorio

unread,
Oct 20, 2010, 10:03:35 AM10/20/10
to
> [...]

> 2. I can see why GL insists on a solution using \dimexpr. If you do
>
> \edef\test{\number\dimexpr 4.45pt}
>
> you get 291635. Where has the unit (pt) gone? Was it removed by
> \number? Does \number do any rounding up or down? I couldn't find the
> answer in the TeXBook and the eTeX manual.
>
> [...]

Any dimension in TeX is an integer number of scaled points; there
are 65536 scaled point in 1pt.

Without delving in the mysteries of \number, suffice it to say
that when \number is followed by a \dimen register (it may be also
an internal dimension such as \hsize) or a \skip register, it uses
as a number the value of the dimension in scaled points.

With e-TeX \dimexpr can be used where TeX looks for a dimension
and so also after \number. So in your \test you get the number
of scaled points corresponding to 4.45pt; indeed, bc gives

291635/65536 = 4.44999694824218750000

The same holds in all cases when TeX is expecting a <number>,
thus also after \ifnum. For example \count255=\hsize will load
into \count255 the value of \hsize in scaled points.

\ifnum\dimexpr...\relax<\dimexpr...\relax works because scaling
dimensions preserves the order. However \ifnum 4.5pt<6pt wouldn't
work because the search for a number would stop at "4", which is
not followed by <, = or >. But \dimexpr changes effectively this
into a kind of "virtual dimension register".

Ciao
Enrico

Donald Arseneau

unread,
Oct 20, 2010, 5:11:03 PM10/20/10
to
Pluto <a.m...@rocketmail.com> writes:

> \def\ifnnum#1\then{\ifdim\@ptlt\@ptgt\@pteq #1pt\@nilgobble<=>
> \LTS@nil}

One problem is that dimensions are followed by one optional space,
so TeX will consume a space from the text that is meant to be
typeset.

Using \@pt instead of "pt" would prevent that, but would then
make an empty argument be treated the same as "1". (Could be
a feature in some cases.)

If we simply insert a space after the "pt" then we don't expand
\@nilgobble (not looking for a space anymore). So we have to
put the space at the very end. Since ordinary spaces are ignored
after commands, we'll use \space

\def\ifnnum#1\then{\ifdim\@ptlt\@ptgt\@pteq #1pt\@nilgobble

<=>\LTS@nil\space}


--
Donald Arseneau as...@triumf.ca

Will Robertson

unread,
Oct 21, 2010, 5:09:12 AM10/21/10
to
On Oct 15, 3:44 am, Pluto <a.m...@rocketmail.com> wrote:
> TeX's \ifnum accepts integers only. Is there a corresponding test for
> real numbers, eg \ifnum 0.5<1\fi?

Although you've now received satisfactory solutions for comparisons
within TeX's ability to represent real numbers to a precision of
around 1/65536, Joseph Wright has recently been writing a floating-
point math package for LaTeX3 which you can also use if you need to
compare very large or very small numbers. For example:

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\fp_compare:nNnTF {0.1} < {0.2} {T}{F}
\fp_compare:nNnTF {100000000.1} < {100000000.2} {T}{F}
\fp_compare:nNnTF {0.123456789e55} < {0.223456789e55} {T}{F}
\fp_compare:nNnTF {0.123456789e-55} < {0.223456789e-55} {T}{F}
\ExplSyntaxOff
\end{document}

Will

GL

unread,
Oct 21, 2010, 6:12:37 AM10/21/10
to
Le 21/10/2010 11:09, Will Robertson a écrit :
> On Oct 15, 3:44 am, Pluto<a.m...@rocketmail.com> wrote:
>> TeX's \ifnum accepts integers only. Is there a corresponding test for
>> real numbers, eg \ifnum 0.5<1\fi?
>
> Although you've now received satisfactory solutions for comparisons
> within TeX's ability to represent real numbers to a precision of
> around 1/65536, Joseph Wright has recently been writing a floating-
> point math package for LaTeX3 which you can also use if you need to
> compare very large or very small numbers. For example:

Not very very much fond of LaTeX3 syntax...

Very very very much annoid by LaTeX3 log file...

No control on LaTeX3 ;-(

Sorry ;-( again ;-(

Taco Hoekwater

unread,
Oct 21, 2010, 8:56:14 AM10/21/10
to Will Robertson
On 10/21/2010 11:09 AM, Will Robertson wrote:
> On Oct 15, 3:44 am, Pluto<a.m...@rocketmail.com> wrote:
>> TeX's \ifnum accepts integers only. Is there a corresponding test for
>> real numbers, eg \ifnum 0.5<1\fi?
>
> Although you've now received satisfactory solutions for comparisons
> within TeX's ability to represent real numbers to a precision of
> around 1/65536, Joseph Wright has recently been writing a floating-
> point math package for LaTeX3 which you can also use if you need to
> compare very large or very small numbers.

And while at it, here is a luatex solution:


\def\ifreal#1{\directlua{if #1 then
tex.sprint([[\noexpand\iftrue]])
else
tex.sprint ([[\noexpand\iffalse]])
end }}

\ifreal {1.205<1.204}T\else F\fi

Best wishes,
Taco

zappathustra

unread,
Oct 21, 2010, 9:30:05 AM10/21/10
to

Yes but no, for reasons already mentioned (\ifreal is not a real \if
matching \else and \fi), but that can be amended easily.
But the interesting point here (at least to me) is your double bracket
notation: is that the so-called long bracket notation in Lua, and does
it do the job of \luaescapestring?

Best,
Paul


Taco Hoekwater

unread,
Oct 21, 2010, 9:44:16 AM10/21/10
to zappathustra
On 10/21/2010 03:30 PM, zappathustra wrote:
>>
>>
>> \def\ifreal#1{\directlua{if #1 then
>> tex.sprint([[\noexpand\iftrue]])
>> else
>> tex.sprint ([[\noexpand\iffalse]])
>> end }}
>>
>> \ifreal {1.205<1.204}T\else F\fi
>
> Yes but no, for reasons already mentioned (\ifreal is not a real \if
> matching \else and \fi), but that can be amended easily.

I would have done it that way except that I don't remember the
latex version of \firstoftwoarguments / \secondoftwoarguments :)

> But the interesting point here (at least to me) is your double bracket
> notation: is that the so-called long bracket notation in Lua

Yes.

> and does it do the job of \luaescapestring?

Actually more like the reverse. [[..]] strings do not support escapes
at all, so the \ is just that, a backslash. Using [[..]] strings
generally does not work with user-supplied content (as the content
should not contain "]]") but if you need just a literal, it is often
more convenient than \luaescapestring.

Will Robertson

unread,
Oct 21, 2010, 10:49:31 AM10/21/10
to
On Oct 21, 8:12 pm, GL <gouail...@gmail.com> wrote:
> Le 21/10/2010 11:09, Will Robertson a écrit :
>
> > On Oct 15, 3:44 am, Pluto<a.m...@rocketmail.com>  wrote:
> >> TeX's \ifnum accepts integers only. Is there a corresponding test for
> >> real numbers, eg \ifnum 0.5<1\fi?
>
> > Although you've now received satisfactory solutions for comparisons
> > within TeX's ability to represent real numbers to a precision of
> > around 1/65536, Joseph Wright has recently been writing a floating-
> > point math package for LaTeX3 which you can also use if you need to
> > compare very large or very small numbers. For example:
>
> Not very very much fond of LaTeX3 syntax...
>
> Very very very much annoid by LaTeX3 log file...
>
> No control on LaTeX3 ;-(

I'm not really sure what you mean here.
The syntax is a matter of opinion, but the log file is certainly
something we can deal with. But what's the annoyance?

Cheers,
Will

zappathustra

unread,
Oct 21, 2010, 11:06:57 AM10/21/10
to
Le 21/10/2010 15:44, Taco Hoekwater a écrit :
> On 10/21/2010 03:30 PM, zappathustra wrote:
>>>
>>>
>>> \def\ifreal#1{\directlua{if #1 then
>>> tex.sprint([[\noexpand\iftrue]])
>>> else
>>> tex.sprint ([[\noexpand\iffalse]])
>>> end }}
>>>
>>> \ifreal {1.205<1.204}T\else F\fi
>>
>> Yes but no, for reasons already mentioned (\ifreal is not a real \if
>> matching \else and \fi), but that can be amended easily.
>
> I would have done it that way except that I don't remember the
> latex version of \firstoftwoarguments / \secondoftwoarguments :)
>

You don't even need that, thanks to Donald's solution somewhere in this
thread:

\let\then\iffalse
\def\ifreal#1\then{...}

So you can use \ifreal...\then ...\else ...\fi

(I think in LaTeX it's \@firstoftwo, \@secondoftwo. But I don't use it
either, so I don't know.)

>> But the interesting point here (at least to me) is your double bracket
>> notation: is that the so-called long bracket notation in Lua
>
> Yes.
>
>> and does it do the job of \luaescapestring?
>
> Actually more like the reverse. [[..]] strings do not support escapes
> at all, so the \ is just that, a backslash. Using [[..]] strings
> generally does not work with user-supplied content (as the content
> should not contain "]]") but if you need just a literal, it is often
> more convenient than \luaescapestring.

I can see that. Pretty useful.

Paul

Heiko Oberdiek

unread,
Oct 21, 2010, 12:43:22 PM10/21/10
to
Taco Hoekwater <ta...@elvenkind.com> wrote:

> On 10/21/2010 03:30 PM, zappathustra wrote:
> >>
> >>
> >> \def\ifreal#1{\directlua{if #1 then
> >> tex.sprint([[\noexpand\iftrue]])
> >> else
> >> tex.sprint ([[\noexpand\iffalse]])
> >> end }}
> >>
> >> \ifreal {1.205<1.204}T\else F\fi

> > But the interesting point here (at least to me) is your double bracket


> > notation: is that the so-called long bracket notation in Lua
>
> Yes.
>
> > and does it do the job of \luaescapestring?
>
> Actually more like the reverse. [[..]] strings do not support escapes
> at all, so the \ is just that, a backslash. Using [[..]] strings
> generally does not work with user-supplied content (as the content
> should not contain "]]") but if you need just a literal, it is often
> more convenient than \luaescapestring.

However, the code depends on \escapechar. That can be cured by

\begingroup
\lccode`0=92 % backslash
\lowercase{\endgroup
\def\BackslashChar{0}%
}
...
tex.sprint([[\BackslashChar iftrue]])
...

--
Heiko Oberdiek

Pluto

unread,
Oct 22, 2010, 8:57:14 AM10/22/10
to

I must say thank you for this. Where can I find more information about
it? The TeXBook? Pages?

Dan Luecking

unread,
Oct 22, 2010, 1:22:32 PM10/22/10
to

The TeXbook covers only TeX, and \dimexpr is an eTeX
extension. See the etex manual: etex-man.pdf, page 8.
Also page 17.

Like the TeXbook, the etex manual doesn't make everything
explicit. For example, that a \dimexpr is terminated by
anything that does not satisfy the given syntax (e.g.,
by the >, =, or < in an \ifdim). And if that terminating
thing is \relax, it is removed, otherwise not.

I don't see anywhere stated that a "virtual dimension
register" is returned. However, in the syntax for
<internal dimension>
on page 17, one of the things listed is
\dimexpr<dimen expr><optional spaces and \relax>
So syntactically, it is an <internal dimension>.

Like the TeXbook, some things have to be experimented
with to be truly understood. Just try things that can be
done with other kinds of dimensions:
\the\dimexpr 2pt + 3pt\relax
or
\dimen0=2\dimexpr 2pt + 3pt\relax
These reveal that it acts for most purposes like other
things that return dimensions: internal parameters,
\fontdimen<font>, \dimen0, etc.


Dan
To reply by email, change LookInSig to luecking

GL

unread,
Oct 22, 2010, 2:16:16 PM10/22/10
to
Le 20/10/2010 16:03, Enrico Gregorio a �crit :

>> [...]
>
> Any dimension in TeX is an integer number of scaled points; there
> are 65536 scaled point in 1pt.

Yes and this `feature' (which is the beast's internal design) can
be used to do arithmetics with huge numbers:

\number\dimexpr Xin * D /4736286 \relax

multiplies X by D, and with e-TeX scaling computation on
intermediate 64 bits integers, you are sure that if X is
in the range
-\maxdimen < X < \maxdimen
the \dimexpr Xin * D / 4736286\relax will never break
for "dimension too large": only the result limits the
range of possible values and the chosen dimension
(inches here) limit the possible values:

\def\product#1#2{\number\dimexpr #1in * #2 / 4736286\relax}

#1 is limited to \maxdimen inches
that is (2^30-1)/4736286 = 226
and the result as well, that is:
#2 <= 226 * 4736286 / (max #1=226) = 47236286

Fair an easy to remember isn't it ?

\number\dimexpr #1pt * #2/65536\relax is limited to
#1 <= \maxdimen = 16383 (pt) = 2^30/65536-1 (in sp->pt)
and the result is limited to 65536.


X * D / 4736286 < 2^30-1
<=> x * D < 2^37 - 1 ~= 5 x 10^15 (in sp)

4736286 is the magic number: \number\dimexpr1in

> \ifnum\dimexpr...\relax<\dimexpr...\relax works because scaling
> dimensions preserves the order. However \ifnum 4.5pt<6pt wouldn't

Not really the reason: \number\dimexpr *is not a scaling*, it's
the internal expression of a dimension (in sp, the "real unit").

> work because the search for a number would stop at "4", which is
> not followed by<, = or>. But \dimexpr changes effectively this
> into a kind of "virtual dimension register".

Not "virtual" but "very foundamentaly real dimension register!".
Fixed point arithmetic is in fact integer arithmetic (that is
just _arithmetic_, briefly). When you have to check the range
of number your code can handle, best is to go into scaled point.
Then the dimensions are just like 31-bits signed integers...

> Ciao
> Enrico

Will Robertson

unread,
Oct 22, 2010, 11:43:41 PM10/22/10
to
On Oct 22, 2:43 am, Heiko Oberdiek <heiko.oberd...@googlemail.com>
wrote:

> However, the code depends on \escapechar. That can be cured by
>
> \begingroup
>   \lccode`0=92 % backslash
> \lowercase{\endgroup
>   \def\BackslashChar{0}%}
>
> ...
>   tex.sprint([[\BackslashChar iftrue]])


Good to keep in mind. In this particular application, what about using
this instead?

\csname\directlua{... tex.sprint(iftrue) ...}\endcsname

I guess it's a bit messy combining TeX and Lua in such a way.

Will

Heiko Oberdiek

unread,
Oct 23, 2010, 3:38:13 AM10/23/10
to
Will Robertson <wsp...@gmail.com> wrote:

> On Oct 22, 2:43�am, Heiko Oberdiek <heiko.oberd...@googlemail.com>
> wrote:
> > However, the code depends on \escapechar. That can be cured by
> >
> > \begingroup
> > � \lccode`0=92 % backslash
> > \lowercase{\endgroup
> > � \def\BackslashChar{0}%}
> >
> > ...
> > � tex.sprint([[\BackslashChar iftrue]])
>
>
> Good to keep in mind. In this particular application, what about using
> this instead?
>
> \csname\directlua{... tex.sprint(iftrue) ...}\endcsname

Even better, it also makes the code independent from the
catcode of the backslash when using the macro.

--
Heiko Oberdiek

zappathustra

unread,
Oct 23, 2010, 4:37:01 AM10/23/10
to
Le 23/10/2010 09:38, Heiko Oberdiek a �crit :

Good idea. Not a reason to forget about quotes, though :)

\csname\directlua{... tex.sprint("iftrue") ...}\endcsname

Anyway it has the drawback of being limited to one control sequence, and
to me it sounds a little bit too dirty-trickish... I'd rather go for:

\directlua{%
make_cs = function (name)
return string.char(tex.escapechar) .. name
end
}

And then:

\directlua{%
... tex.sprint(make_cs("iftrue")) ...
}

And you can accommodate multiple arguments.

Best,
Paul

Will Robertson

unread,
Oct 23, 2010, 5:54:13 AM10/23/10
to
On Oct 23, 6:37 pm, zappathustra <zappathus...@free.fr> wrote:
>
> \csname\directlua{... tex.sprint("iftrue") ...}\endcsname

Thanks for pointing out my oops :)

> Anyway it has the drawback of being limited to one control sequence, and
> to me it sounds a little bit too dirty-trickish... I'd rather go for:

> [snip]


>    ... tex.sprint(make_cs("iftrue")) ...

Best of the lot.
A good candidate for your upcoming "LuaTeX by Topic" :)

W

zappathustra

unread,
Oct 23, 2010, 6:11:05 AM10/23/10
to

Le 23/10/2010 11:54, Will Robertson a �crit :


> On Oct 23, 6:37 pm, zappathustra<zappathus...@free.fr> wrote:
>> \csname\directlua{... tex.sprint("iftrue") ...}\endcsname
> Thanks for pointing out my oops :)
>

``Oops'' as a noun, nice example of improper derivation (``improper''
not being pejorative here!). I write it down for my course on morphology :)

>> Anyway it has the drawback of being limited to one control sequence, and
>> to me it sounds a little bit too dirty-trickish... I'd rather go for:
>> [snip]
>> ... tex.sprint(make_cs("iftrue")) ...
> Best of the lot.
> A good candidate for your upcoming "LuaTeX by Topic" :)
>

Mine? And but so errrrrr ... Saint Eijkhout, have mercy!

Paul

Taco Hoekwater

unread,
Oct 23, 2010, 6:31:07 AM10/23/10
to Will Robertson
On 10/23/2010 11:54 AM, Will Robertson wrote:
>
>> Anyway it has the drawback of being limited to one control sequence, and
>> to me it sounds a little bit too dirty-trickish... I'd rather go for:
>
>> [snip]
>> ... tex.sprint(make_cs("iftrue")) ...
>
> Best of the lot.
> A good candidate for your upcoming "LuaTeX by Topic" :)

I agree, that is a really good solution.

An alternative approach is to use an explicit catcode tables as the
first argument of tex.sprint (plain code below is using luatexbase-
cctb.sty from the luatexbase package).

\input luatexbase-cctb.sty


\def\ifreal#1{\directlua{if #1 then

tex.sprint(\the\CatcodeTableLaTeX,[[\noexpand\iftrue]])
else
tex.sprint (\the\CatcodeTableLaTeX,[[\noexpand\iffalse]])
end }}


Taco

Donald Arseneau

unread,
Oct 23, 2010, 8:25:06 AM10/23/10
to
GL <goua...@gmail.com> writes:

> > into a kind of "virtual dimension register".
>
> Not "virtual" but "very foundamentaly real dimension register!".

Fundamentally real dimension, but not really a register.

I hadn't realized before this discussion that \the\dimexpr... and
1.2\dimexpr... were valid.

--
Donald Arseneau as...@triumf.ca

GL

unread,
Oct 23, 2010, 8:34:50 AM10/23/10
to
Le 23/10/2010 14:25, Donald Arseneau a écrit :
> GL<goua...@gmail.com> writes:
>
>>> into a kind of "virtual dimension register".
>>
>> Not "virtual" but "very foundamentaly real dimension register!".
>
> Fundamentally real dimension, but not really a register.
>
> I hadn't realized before this discussion that \the\dimexpr... and
> 1.2\dimexpr... were valid.

Yes, and wish you divide 2.5 by 8.3 ?
Then:

\the\dimexpr
\strip@pt\dimexpr 2.5in*65536/\number\dimexpr8.3in\relax\relax
\p@\relax

In inches, the range of input is limited (\maxdimen<227 in) but
precision's better.

zappathustra

unread,
Oct 23, 2010, 9:10:54 AM10/23/10
to
Le 23/10/2010 12:31, Taco Hoekwater a �crit :

Better still, since now we don't have to worry about catcodes in
general, not simply \escapechar. I think this catcode table thing in
tex.sprint will be very useful for manipulations with verbatim material.

Paul

Heiko Oberdiek

unread,
Oct 23, 2010, 10:39:30 AM10/23/10
to
zappathustra <zappat...@free.fr> wrote:

That reintroduces the dependency of the catcode for the escapechar
again. Thus the code must test that
* tex.escapechar isn't suppressed (<0)
* catcode of tex.escapechar is 0

Therefore I like the former solution using


\csname\directlua{...tex.sprint("iftrue")...}\endcsname

--
Heiko Oberdiek

zappathustra

unread,
Oct 23, 2010, 10:47:37 AM10/23/10
to

Le 23/10/2010 16:39, Heiko Oberdiek a �crit :

You're absolutely right (as usual, I'd say...). I frequently think that
\escapechar returns the character with category 0, which is absolutely
not the case (to begin with, there might be no such character, or
several). I can't seem to stop doing that mistake. So much for /LuaTeX
by Topic/...

Taco's solution was better anyway, since it deals with all category
codes at once.

Best,
Paul

0 new messages