22

In my use of \numexpr's integer division / I have never gotten any advantage from the fact that it rounds rather than truncates. TeX's own \divide truncates.

If we were dealing with floating point numbers, I could understand. But here the operations are on integers. Again, any time this has been an issue for me in coding, always and systematically the "rounding" was a nuisance and generated extra coding to get around it.

Is there any reason rounding has been preferred to truncating?


To be completely honest, I must report there was finally one occasion when I exploited the rounding feature: in the \xintDSRr macro of xint 1.2i (2016/12/13). This is a macro which given a (long) integer N, computes N/10 rounded away from zero (i.e. it extends the N/10 of \numexpr to big integers). The macro is mainly there for internal use by \xintiiDivRound which is the general macro computing the rounding to an integer of general (big) fractions N/M. In all the thousands of lines of xint, with zillions of use of \numexpr, I think this is about the only place where I felt happy that the / operator rounds inside \numexpr. In view of what these macros do, it is not that surprising!

On the confirmation of bad experience, but I have now forgotten the precise details, I think I got particularly annoyed once by the fact that when doing a "scaling" operation a*b/c where the product can be double-word without raising arithmetic overflow, the division is rounding. I think I wanted to use it for c a power of ten, and if it had been truncating I could have sped up some core algorithms in xint, but I only have left in memory transient feelings, I forgot the details.

  • as an example of a nuisance (#1/#2)*#2 can generate arithmetic overflow. – user4686 Nov 23 '13 at 11:08
  • 4
    Because so was decided by the programmer. – egreg Nov 23 '13 at 11:13
  • 3
    To me it's clearly a bug. Someone chose the wrong operation when implementing it. – Stephan Lehmke Nov 23 '13 at 11:14
  • 3
    Rounding integer division is totally unheard of in programming AFAIK. – Stephan Lehmke Nov 23 '13 at 11:22
  • 1
    @JosephWright Integer division has had had a clearly fixed meaning for forever in programming. What seems natural to non-programmers doesn't come into it, the same way as it doesn't make sense to discuss the "naturality" of the meaning of "onto" in mathematics. – Stephan Lehmke Nov 23 '13 at 12:11
14

This is the relevant part from etex.ch:

5247    @ The function |quotient(n,d)| computes the rounded quotient
5248    $q=\lfloor n/d+{1\over2}\rfloor$, when $n$ and $d$ are positive.
5249    
5250    @<Declare subprocedures for |scan_expr|@>=
5251    function quotient(@!n,@!d:integer):integer;
5252    var negative:boolean; {should the answer be negated?}
5253    @!a:integer; {the answer}
5254    begin if d=0 then num_error(a)
5255    else  begin if d>0 then negative:=false
5256      else  begin negate(d); negative:=true;
5257        end;
5258      if n<0 then
5259        begin negate(n); negative:=not negative;
5260        end;
5261      a:=n div d; n:=n-a*d; d:=n-d; {avoid certain compiler optimizations!}
5262      if d+n>=0 then incr(a);
5263      if negative then negate(a);
5264      end;
5265    quotient:=a;
5266    end;
5267    
5268    @ Here the term |t| is multiplied by the quotient $n/f$.
5269    
5270    @d expr_s(#)==#:=fract(#,n,f,max_dimen)
5271    
5272    @<Cases for evaluation of the current term@>=
5273    expr_scale: if l=int_val then t:=fract(t,n,f,infinity)
5274      else if l=dimen_val then expr_s(t)
5275      else  begin expr_s(width(t)); expr_s(stretch(t)); expr_s(shrink(t));
5276        end;
5277    
5278    @ Finally, the function |fract(x,n,d,max_answer)| computes the integer
5279    $q=\lfloor xn/d+{1\over2}\rfloor$, when $x$, $n$, and $d$ are positive
5280    and the result does not exceed |max_answer|.  We can't use floating
5281    point arithmetic since the routine must produce identical results in all
5282    cases; and it would be too dangerous to multiply by~|n| and then divide
5283    by~|d|, in separate operations, since overflow might well occur.  Hence
5284    this subroutine simulates double precision arithmetic, somewhat
5285    analogous to \MF's |make_fraction| and |take_fraction| routines.
5286    
5287    @d too_big=88 {go here when the result is too big}

Why Peter Breitenlohner decided to round rather than truncate is not explained. There may have been discussion on the matter in the NTS group. It surely is a nuisance having similar operations that can give different results.

In any case the behavior is clearly documented in etex_man.tex:

454 The arithmetic performed by \eTeX's expressions does not do much that could
455 not be done by \TeX's arithmetic operations \|\advance|, \|\multiply|, and
456 \|\divide|, although there are some notable differences: Each factor is
457 checked to be in the allowed range, numbers must be less than $2^{31}$ in
458 absolute value, dimensions or glue components must be less than
459 $2^{14}$\[pt], \[mu], \[fil], etc.\ respectively. The arithmetic operations
460 are performed individually, except for `scaling' operations (a
461 multiplication immediately followed by a division) which are performed as
462 one combined operation with a 64-bit product as intermediate value. The
463 result of each operation is again checked to be in the allowed range.
464 Finally the results of divisions and scalings are rounded, whereas \TeX's
465 \|\divide| truncates.

enter image description here

In expl3 there are

\int_div_round:nn
\int_div_truncate:nn

so that one always knows what's the used division.

|improve this answer|||||
  • 1
    too bad there are no explanations, but this is already informative. Using rounding gives a slightly faster Euclide algorithm to get to the gcd allowing negative remainders, always less than half the size of the previous one. I was wondering if something of this type could have been the rationale. – user4686 Nov 23 '13 at 11:41
  • Indeed, one of the reasons we removed some old code from expl3 which re-implemented the calc package was because it tried to use the primitive \numexpr and we got different results in some cases! – Joseph Wright Nov 23 '13 at 11:41
  • 1
    Note implementing (expandable) \int_div_truncate:nn is non-trivial! – Joseph Wright Nov 23 '13 at 11:46
  • 1
    @JosephWright Note also that implementing (expandable) \int_div_round:nn would be trivial if we had expandable integer division. – Stephan Lehmke Nov 23 '13 at 12:10
  • @jfbu I have a strong suspicion that the authors were thinking modulo operation which has truncation and floored flavors. See Wiki about it : Remainder Calculation. Doesn't answer but might help understanding the strange choice. – percusse Nov 23 '13 at 17:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy