% \iffalse meta-comment
%
%% File: l3fp-round.dtx Copyright(C) 2011-2012,2014 The LaTeX3 Project
%%
%% It may be distributed and/or modified under the conditions of the
%% LaTeX Project Public License (LPPL), either version 1.3c of this
%% license or (at your option) any later version.  The latest version
%% of this license is in the file
%%
%%    http://www.latex-project.org/lppl.txt
%%
%% This file is part of the "l3kernel bundle" (The Work in LPPL)
%% and all files in that bundle must be distributed together.
%%
%% The released version of this bundle is available from CTAN.
%%
%% -----------------------------------------------------------------------
%%
%% The development version of the bundle can be found at
%%
%%    http://www.latex-project.org/svnroot/experimental/trunk/
%%
%% for those people who are interested.
%%
%%%%%%%%%%%
%% NOTE: %%
%%%%%%%%%%%
%%
%%   Snapshots taken from the repository represent work in progress and may
%%   not work or may contain conflicting material!  We therefore ask
%%   people _not_ to put them into distributions, archives, etc. without
%%   prior consultation with the LaTeX Project Team.
%%
%% -----------------------------------------------------------------------
%%
%
%<*driver>
\documentclass[full]{l3doc}
\GetIdInfo$Id: l3fp-round.dtx 5354 2014-08-23 01:35:39Z bruno $
  {L3 Floating-point rounding}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{^^A
%   The \textsf{l3fp-round} package\\ Rounding floating points^^A
%   \thanks{This file describes v\ExplFileVersion,
%     last revised \ExplFileDate.}^^A
% }
%
% \author{^^A
%  The \LaTeX3 Project\thanks
%    {^^A
%      E-mail:
%        \href{mailto:latex-team@latex-project.org}
%          {latex-team@latex-project.org}^^A
%    }^^A
% }
%
% \date{Released \ExplFileDate}
%
% \maketitle
%
% \begin{documentation}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3fp-round} implementation}
%
%    \begin{macrocode}
%<*initex|package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=fp>
%    \end{macrocode}
%
% ^^A todo: provide an interface for rounding modes.
% ^^A todo: provide a \l_@@_rounding_mode_int giving the current mode.
% ^^A todo: make transcendental function obey the correct rounding mode.
% ^^A todo: optimize all rounding functions for various rounding modes.
% ^^A todo: reduce the number of almost identical functions.
%
% \subsection{Rounding tools}
%
% Floating point operations often yield a result that cannot be exactly
% represented in a significand with $16$ digits.  In that case, we need to
% round the exact result to a representable number.  The \textsc{ieee}
% standard defines four rounding modes:
% \begin{itemize}
%   \item Round to nearest: round to the representable floating point
%     number whose absolute difference with the exact result is the
%     smallest.  If the exact result lies exactly at the mid-point
%     between two consecutive representable floating point numbers,
%     round to the floating point number whose last digit is even.
%   \item Round towards negative infinity: round to the greatest
%     floating point number not larger than the exact result.
%   \item Round towards zero: round to a floating point number with the
%     same sign as the exact result, with the largest absolute value not
%     larger than the absolute value of the exact result.
%   \item Round towards positive infinity: round to the least floating
%     point number not smaller than the exact result.
% \end{itemize}
% This is not fully implemented in \pkg{l3fp} yet, and transcendental
% functions fall back on the \enquote{round to nearest} mode.  All
% rounding for basic algebra is done through the functions defined in
% this module, which can be redefined to change their rounding behaviour
% (but there is not interface for that yet).
%
% The rounding tools available in this module are many variations on a
% base function \cs{@@_round:NNN}, which expands to \cs{c_zero} or
% \cs{c_one} depending on whether the final result should be rounded up
% or down.
% \begin{itemize}
%   \item \cs{@@_round:NNN} \meta{sign} \meta{digit_1} \meta{digit_2}
%     can expand to \cs{c_zero} or \cs{c_one}.
%   \item \cs{@@_round_s:NNNw} \meta{sign} \meta{digit_1} \meta{digit_2}
%     \meta{more digits}|;| can expand to |\c_zero ;| or |\c_one ;|.
%   \item \cs{@@_round_neg:NNN} \meta{sign} \meta{digit_1} \meta{digit_2}
%     can expand to \cs{c_zero} or \cs{c_one}.
% \end{itemize}
% See implementation comments for details on the syntax.
%
% \begin{macro}[int, rEXP]{\@@_round:NNN}
% \begin{macro}[aux, rEXP]
%   {
%     \@@_round_to_nearest:NNN,
%     \@@_round_to_ninf:NNN,
%     \@@_round_to_zero:NNN,
%     \@@_round_to_pinf:NNN
%   }
%   \begin{syntax}
%     \cs{@@_round:NNN} \meta{final sign} \meta{digit_1} \meta{digit_2}
%   \end{syntax}
%   If rounding the number $\meta{final sign}
%   \meta{digit_1}.\meta{digit_2}$ to an integer rounds it towards zero
%   (truncates it), this function expands to \cs{c_zero}, and otherwise
%   to \cs{c_one}.  Typically used within the scope of an
%   \cs{__int_eval:w}, to add~$1$ if needed, and thereby round
%   correctly.  The result depends on the rounding mode.
%
%   It is very important that \meta{final sign} be the final sign of the
%   result. Otherwise, the result will be incorrect in the case of
%   rounding towards~$-\infty$ or towards~$+\infty$. Also recall that
%   \meta{final sign} is~$0$ for positive, and~$2$ for negative.
%
%   By default, the functions below return \cs{c_zero}, but this is
%   superseded by \cs{@@_round_return_one:}, which instead returns
%   \cs{c_one}, expanding everything and removing \cs{c_zero} in the
%   process.  In the case of rounding towards~$\pm\infty$ or
%   towards~$0$, this is not really useful, but it prepares us for the
%   \enquote{round to nearest, ties to even} mode.
%
%   The \enquote{round to nearest} mode is the default.  If the
%   \meta{digit_2} is larger than~$5$, then round up.  If it is less
%   than~$5$, round down.  If it is exactly $5$, then round such that
%   \meta{digit_1} plus the result is even.  In other words, round up if
%   \meta{digit_1} is odd.
%    \begin{macrocode}
\cs_new:Npn \@@_round_return_one:
  { \exp_after:wN \c_one \tex_romannumeral:D }
\cs_new:Npn \@@_round_to_ninf:NNN #1 #2 #3
  {
    \if_meaning:w 2 #1
      \if_int_compare:w #3 > \c_zero
        \@@_round_return_one:
      \fi:
    \fi:
    \c_zero
  }
\cs_new:Npn \@@_round_to_zero:NNN #1 #2 #3 { \c_zero }
\cs_new:Npn \@@_round_to_pinf:NNN #1 #2 #3
  {
    \if_meaning:w 0 #1
      \if_int_compare:w #3 > \c_zero
        \@@_round_return_one:
      \fi:
    \fi:
    \c_zero
  }
\cs_new:Npn \@@_round_to_nearest:NNN #1 #2 #3
  {
    \if_int_compare:w #3 > \c_five
      \@@_round_return_one:
    \else:
      \if_meaning:w 5 #3
        \if_int_odd:w #2 \exp_stop_f:
          \@@_round_return_one:
        \fi:
      \fi:
    \fi:
    \c_zero
  }
\cs_new_eq:NN \@@_round:NNN \@@_round_to_nearest:NNN
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP, int]{\@@_round_s:NNNw}
%   \begin{syntax}
%     \cs{@@_round_s:NNNw} \meta{final sign} \meta{digit} \meta{more digits} |;|
%   \end{syntax}
%   Similar to \cs{@@_round:NNN}, but with an extra semicolon, this
%   function expands to |\c_zero ;| if rounding $\meta{final sign}
%   \meta{digit}.\meta{more digits}$ to an integer truncates, and to
%   |\c_one ;| otherwise.  The \meta{more digits} part must be a digit,
%   followed by something that does not overflow a \cs{int_use:N}
%   \cs{__int_eval:w} construction.  The only relevant information about
%   this piece is whether it is zero or not.
%    \begin{macrocode}
\cs_new:Npn \@@_round_s:NNNw #1 #2 #3 #4;
  {
    \exp_after:wN \@@_round:NNN
    \exp_after:wN #1
    \exp_after:wN #2
    \int_use:N \__int_eval:w
      \if_int_odd:w 0 \if_meaning:w 0 #3 1 \fi:
                      \if_meaning:w 5 #3 1 \fi:
                \exp_stop_f:
        \if_int_compare:w \__int_eval:w #4 > \c_zero
          1 +
        \fi:
      \fi:
      #3
    ;
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int, EXP]{\@@_round_digit:Nw}
%   \begin{syntax}
%     \cs{__int_value:w} \cs{@@_round_digit:Nw} \meta{digit} \meta{intexpr} |;|
%   \end{syntax}
%   This function should always be called within an \cs{__int_value:w}
%   or \cs{__int_eval:w} expansion; it may add an extra
%   \cs{__int_eval:w}, which means that the integer or integer
%   expression should not be ended with a synonym of \tn{relax}, but
%   with a semi-colon for instance.
%    \begin{macrocode}
\cs_new:Npn \@@_round_digit:Nw #1 #2;
  {
    \if_int_odd:w \if_meaning:w 0 #1 \c_one \else:
                  \if_meaning:w 5 #1 \c_one \else:
                  \c_zero \fi: \fi:
      \if_int_compare:w \__int_eval:w #2 > \c_zero
        \__int_eval:w \c_one +
      \fi:
    \fi:
    #1
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int, EXP]{\@@_round_neg:NNN}
% \begin{macro}[aux, EXP]
%   {
%     \@@_round_to_nearest_neg:NNN,
%     \@@_round_to_ninf_neg:NNN,
%     \@@_round_to_zero_neg:NNN,
%     \@@_round_to_pinf_neg:NNN
%   }
%   \begin{syntax}
%     \cs{@@_round_neg:NNN} \meta{final sign} \meta{digit_1} \meta{digit_2}
%   \end{syntax}
%   This expands to \cs{c_zero} or \cs{c_one} after doing the following
%   test. Starting from a number of
%   the form $ \meta{final sign}0.\meta{15 digits}\meta{digit_1} $ with exactly
%   $15$ (non-all-zero) digits before \meta{digit_1}, subtract from it
%   $\meta{final sign}0.0\ldots{}0\meta{digit_2}$, where there are $16$~zeros.
%   If in the current rounding mode the result should be rounded down,
%   then this function returns \cs{c_one}. Otherwise, \emph{i.e.},
%   if the result is rounded back to the first operand, then this function
%   returns \cs{c_zero}.
%
%   It turns out that this negative \enquote{round to nearest}
%   is identical to the positive one. And this is the default mode.
%    \begin{macrocode}
\cs_new:Npn \@@_round_to_ninf_neg:NNN #1 #2 #3
  {
    \if_meaning:w 0 #1
      \if_int_compare:w #3 > \c_zero
        \@@_round_return_one:
      \fi:
    \fi:
    \c_zero
  }
\cs_new:Npn \@@_round_to_zero_neg:NNN #1 #2 #3
  {
    \if_int_compare:w #3 > \c_zero
      \@@_round_return_one:
    \fi:
    \c_zero
  }
\cs_new:Npn \@@_round_to_pinf_neg:NNN #1 #2 #3
  {
    \if_meaning:w 2 #1
      \if_int_compare:w #3 > \c_zero
        \@@_round_return_one:
      \fi:
    \fi:
    \c_zero
  }
\cs_new_eq:NN \@@_round_to_nearest_neg:NNN \@@_round_to_nearest:NNN
\cs_new_eq:NN \@@_round_neg:NNN \@@_round_to_nearest_neg:NNN
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsection{The \texttt{round} function}
%
% ^^A todo: This macro is intermingled with l3fp-parse.
% ^^A todo: Add explanations.
% \begin{macro}[aux,EXP]{\@@_round_o:Nw}
%   This function expects one or two arguments.
%    \begin{macrocode}
\cs_new:Npn \@@_round_o:Nw #1#2 @
  {
    \if_case:w
      \__int_eval:w \@@_array_count:n {#2} - \c_one \__int_eval_end:
         \@@_round:Nwn #1 #2 {0} \tex_romannumeral:D
    \or: \@@_round:Nww #1 #2 \tex_romannumeral:D
    \else:
      \@@_error:nffn { num-args }
        { \@@_round_name_from_cs:N #1 () } { 1 } { 2 }
      \exp_after:wN \c_nan_fp \tex_romannumeral:D
    \fi:
    -`0
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, EXP]{\@@_round_name_from_cs:N}
%    \begin{macrocode}
\cs_new:Npn \@@_round_name_from_cs:N #1
  {
    \cs_if_eq:NNTF #1 \@@_round_to_zero:NNN { trunc }
      {
        \cs_if_eq:NNTF #1 \@@_round_to_ninf:NNN { floor }
          {
            \cs_if_eq:NNTF #1 \@@_round_to_pinf:NNN { ceil }
              { round }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int, EXP]{\@@_round:Nww, \@@_round:Nwn}
% \begin{macro}[aux, EXP]
%   {
%     \@@_round_normal:NwNNnw ,
%     \@@_round_normal:NnnwNNnn ,
%     \@@_round_pack:Nw ,
%     \@@_round_normal:NNwNnn ,
%     \@@_round_normal_end:wwNnn ,
%     \@@_round_special:NwwNnn ,
%     \@@_round_special_aux:Nw
%   }
%    \begin{macrocode}
\cs_new:Npn \@@_round:Nww #1#2 ; #3 ;
  {
    \@@_small_int:wTF #3; { \@@_round:Nwn #1#2; }
      {
        \@@_invalid_operation_tl_o:ff
          { \@@_round_name_from_cs:N #1 }
          { \@@_array_to_clist:n { #2; #3; } }
      }
  }
\cs_new:Npn \@@_round:Nwn #1 \s_@@ \@@_chk:w #2#3#4; #5
  {
    \if_meaning:w 1 #2
      \exp_after:wN \@@_round_normal:NwNNnw
      \exp_after:wN #1
      \__int_value:w #5
    \else:
      \exp_after:wN \@@_exp_after_o:w
    \fi:
    \s_@@ \@@_chk:w #2#3#4;
  }
\cs_new:Npn \@@_round_normal:NwNNnw #1#2 \s_@@ \@@_chk:w 1#3#4#5;
  {
    \@@_decimate:nNnnnn { \c_sixteen - #4 - #2 }
      \@@_round_normal:NnnwNNnn #5 #1 #3 {#4} {#2}
  }
\cs_new:Npn \@@_round_normal:NnnwNNnn #1#2#3#4; #5#6
  {
    \exp_after:wN \@@_round_normal:NNwNnn
    \int_use:N \__int_eval:w
      \if_int_compare:w #2 > \c_zero
        1 \__int_value:w #2
        \exp_after:wN \@@_round_pack:Nw
        \int_use:N \__int_eval:w 1#3 +
      \else:
        \if_int_compare:w #3 > \c_zero
          1 \__int_value:w #3 +
        \fi:
      \fi:
      \exp_after:wN #5
      \exp_after:wN #6
      \use_none:nnnnnnn #3
      #1
      \__int_eval_end:
      0000 0000 0000 0000 ; #6
  }
\cs_new:Npn \@@_round_pack:Nw #1
  { \if_meaning:w 2 #1 + \c_one \fi: \__int_eval_end: }
\cs_new:Npn \@@_round_normal:NNwNnn #1 #2
  {
    \if_meaning:w 0 #2
      \exp_after:wN \@@_round_special:NwwNnn
      \exp_after:wN #1
    \fi:
    \@@_pack_twice_four:wNNNNNNNN
    \@@_pack_twice_four:wNNNNNNNN
    \@@_round_normal_end:wwNnn
    ; #2
  }
\cs_new:Npn \@@_round_normal_end:wwNnn #1;#2;#3#4#5
  {
    \exp_after:wN \@@_exp_after_o:w \tex_romannumeral:D -`0
    \@@_sanitize:Nw #3 #4 ; #1 ;
  }
\cs_new:Npn \@@_round_special:NwwNnn #1#2;#3;#4#5#6
  {
    \if_meaning:w 0 #1
      \@@_case_return:nw
        { \exp_after:wN \@@_zero_fp:N \exp_after:wN #4 }
    \else:
      \exp_after:wN \@@_round_special_aux:Nw
      \exp_after:wN #4
      \int_use:N \__int_eval:w \c_one
        \if_meaning:w 1 #1 -#6 \else: +#5 \fi:
    \fi:
    ;
  }
\cs_new:Npn \@@_round_special_aux:Nw #1#2;
  {
    \exp_after:wN \@@_exp_after_o:w \tex_romannumeral:D -`0
    \@@_sanitize:Nw #1#2; {1000}{0000}{0000}{0000};
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%</initex|package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintChanges
%
% \PrintIndex
