4

I ran the code

\documentclass{article}
\begin{document}
\ExplSyntaxOn
\int_new:N\l_rand
\int_step_inline:nn{50}{
  \int_set:Nn\l_rand{\int_rand:n{56}}
  \int_show:n{\l_rand+(\l_rand-1)/(8-1)}
}
\end{document}

with xetex. In my log shows

> \l_rand +(\l_rand -1)/(8-1)=64.
<recently read> }

which is absurd. Obviously 56+(56-1)/(8-1)=63 and it can't get anywhere larger, so the 64 is a miscalculation. Indeed 55/7 is near 8 so this is probably due to the error of floating point calculation.

So how can I make tex avoid such errors.

user202729
  • 7,143
youthdoo
  • 861

1 Answers1

6

Compare

\documentclass{article}

\begin{document}

\ExplSyntaxOn

\int_eval:n { 1/2 } \par \int_eval:n { 55/7 } \par \int_eval:n { \int_div_truncate:nn { 55 } { 7 } }

\ExplSyntaxOff

\end{document}

This will print

1
8
7

because / rounds to the next integer (ties rounded away from zero). There is also \int_div_round:nn for symmetry, but \int_div_round:nn { 55 } { 7 } is the same as 55/7.

This is documented in the manual and there's nothing one can do about this, because this is what / does in an integer expression according to e-TeX implementation.

enter image description here

Whether this was a good choice can be a matter of debate, but changing the behavior is out of the question, because e-TeX has been like this for about 25 years and a change would break a huge number of documents.

Even using \fp_eval:n wouldn't solve the issue, because you'd need

\fp_eval:n { trunc(55/7,0) }

anyway (at the expense of speed).

egreg
  • 1,121,712
  • Technically it's possible to reimplement int_eval to do floored division, but at a cost of massive loss of performance. And now that int_eval itself is used extensively, changing it now would once again break a lot of things... – user202729 Dec 27 '22 at 13:03
  • @user202729 Just like \fp_eval:n parses its input, yes. But this would require implementing a new parser and the advantages would be really outdone by the disadvantages. – egreg Dec 27 '22 at 13:09
  • A possible engine extension here would be to support something like // for division with truncation; that’s an approach used by E.g. Python – Joseph Wright Dec 27 '22 at 15:50
  • @JosephWright That seems a very good idea! Then Bruno needs to implement also // in fp expressions. – egreg Dec 27 '22 at 15:55
  • I don’t see how changing the third rule (no ( after unary expressions) would affect existing documents. If those documents compiled without error, they would still do so after the update. The only difference could be that some documents might compile without error which didn’t before. I guess that if you had an error in an old document and chose to ignore it, that document would compile differently, but do we honestly need to support that kind of backwards compatibility? – Gaussler Dec 27 '22 at 17:00
  • 1
    @Gaussler I was not referring to the third rule, just at the first one. About the third rule, I agree with you; there must be a reason why the developers added it. – egreg Dec 27 '22 at 17:07