tabularray icon indicating copy to clipboard operation
tabularray copied to clipboard

Table with extendable columns too wide when fixed column width is given in mm

Open FloMiLe opened this issue 2 years ago • 5 comments

The default width of a table with extendable columns is \linewidth. However, I think that the actual width is ever so slightly larger when the width of a fixed-width column is given in mm. When I place such a table inside a center environment (out of habit, but especially) for nice vertical gaps, an increased vertical space above the table signals that the table is actually wider than `\linewidth'.

The code in this MWE

\documentclass{article}

\usepackage{tabularray}

\begin{document}

Column width in pt, expandable columns with full line width:
\begin{center}
\begin{tblr}{ colspec = {Q[l,60pt]X[c]X[c]},
	hlines, vlines }
A1 & B1 & C1  \\
\end{tblr}
\end{center}

Column width in mm, expandable columns with full line width:
\begin{center}
\begin{tblr}{ colspec = {Q[l,20mm]X[c]X[c]},
	hlines, vlines }
A1 & B1 & C1  \\
\end{tblr}
\end{center}

Column width in mm, expandable columns with reduced line width:
\begin{center}
\begin{tblr}{ colspec = {Q[l,20mm]X[c]X[c]}, 
	width=0.99999\linewidth,
	hlines, vlines }
A1 & B1 & C1  \\
\end{tblr}
\end{center}

\end{document}

produces the following result: Screen Shot 2023-07-05 at 23 15 03

Can you confirm this behavior?

FloMiLe avatar Jul 05 '23 21:07 FloMiLe

Looks like some rounding error triggered by special (corner) values. 56.9055pt, what TeX thinks equivalent to 20mm, reproduces the problem too.

Setting \hfuzz=0pt shows (in log) that the constructed table is just 0.00002pt wider than \linewidth

Overfull \hbox (0.00002pt too wide) in paragraph at lines

muzimuzhi avatar Jul 06 '23 08:07 muzimuzhi

The .00002pt rounding error comes from dimension calculations. For example \the\dimexpr100pt-.4pt-.4pt-.4pt gives 98.80002pt, 0.00002pt larger than the expected result.

l3kernel/expl3's l3fp package implements a floating-point arithmetic following IEEE 754, which is more accurate/has more significant figures (up to 16).

In example below, dimension arithmetics (stored in \l__column_target_dim) in \__tblr_collect_extendable_column_width: are replaced with corresponding floating-point ones (stored in \l__column_target_fp), but still kept to show difference of rounding error(s) in error message.

\__tblr_adjust_extendable_column_width_once: and \__tblr_adjust_extendable_column_width_negative: may require similar adjustments.

Full example

\documentclass{article}
\usepackage{tabularray}
\hfuzz=0pt

\makeatletter
\ExplSyntaxOn
\fp_new:N \l__column_target_fp

\cs_gset_protected:Npn \__tblr_collect_extendable_column_width:
  {
    \tl_set:Nx \l_tmpa_tl { \__tblr_prop_item:nn { inner } { width } }
    \tl_if_empty:NTF \l_tmpa_tl
      {
        \dim_set_eq:NN \l__column_target_dim \linewidth
        \fp_set:Nn \l__column_target_fp { \linewidth }
      }
      {
        \dim_set:Nn \l__column_target_dim { \l_tmpa_tl }
        \fp_set:Nn \l__column_target_fp { \l_tmpa_tl }
      }
    \prop_clear:N \l__column_coefficient_prop
    \prop_clear:N \l__column_natural_width_prop
    \prop_clear:N \l__column_computed_width_prop
    \PackageError{test}
      {
        linewidth:~ 
        dim~ \dim_use:N \l__column_target_dim,~ 
        fp~  \fp_use:N  \l__column_target_fp
      }{}%
    \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl
      {
        \tl_set:Nx \l__tblr_a_tl
          { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { width } }
        \tl_set:Nx \l__tblr_b_tl
          { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { coefficient } }
        \tl_set:Nx \l__tblr_c_tl
          { \__tblr_data_item:nen  { column } { \l__tblr_j_tl } { @col-width } }
        \dim_compare:nNnTF { \l__tblr_a_tl } < { 0pt } % column width unset
          {
            \dim_compare:nNnTF { \l__tblr_b_tl pt } = { 0pt }
              {
                \dim_sub:Nn \l__column_target_dim { \l__tblr_c_tl }
                \fp_sub:Nn \l__column_target_fp { \l__tblr_c_tl }
              }
              {
                \prop_put:Nxx \l__column_coefficient_prop
                  { \l__tblr_j_tl } { \l__tblr_b_tl }
                \prop_put:Nxn \l__column_computed_width_prop
                  { \l__tblr_j_tl } { 0pt }
                \dim_compare:nNnF { \l__tblr_b_tl pt } > { 0pt }
                  {
                    \prop_put:Nxx \l__column_natural_width_prop
                      { \l__tblr_j_tl } { \l__tblr_c_tl }
                  }
              }
          }
          {
            \dim_sub:Nn \l__column_target_dim { \l__tblr_a_tl }
            \fp_sub:Nn \l__column_target_fp { \l__tblr_a_tl }
          }
        \tl_set:Nx \l__tblr_a_tl
          { \__tblr_spec_item:ne { vline } { [\l__tblr_j_tl] / @vline-width } }
        \tl_set:Nx \l__tblr_b_tl
          { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { leftsep } }
        \tl_set:Nx \l__tblr_c_tl
          { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { rightsep } }
        \dim_sub:Nn \l__column_target_dim
          { \l__tblr_a_tl + \l__tblr_b_tl + \l__tblr_c_tl }
        \fp_sub:Nn \l__column_target_fp
          { \l__tblr_a_tl + \l__tblr_b_tl + \l__tblr_c_tl }
        \PackageError{test}
          {
            remaining~ width:~
            dim~ \dim_use:N \l__column_target_dim,~
            fp~  \fp_use:N  \l__column_target_fp
          }{}%
      }
    \tl_set:Nx \l__tblr_a_tl
      {
        \__tblr_spec_item:ne { vline }
          { [\int_eval:n {\c@colcount + 1}] / @vline-width }
      }
    \tl_if_empty:NF \l__tblr_a_tl
      {
        \dim_sub:Nn \l__column_target_dim { \l__tblr_a_tl }
        \fp_sub:Nn \l__column_target_fp { \l__tblr_a_tl }
      }
    % use fp calculation result
    \dim_set:Nn \l__column_target_dim
      {
        \fp_to_dim:N \l__column_target_fp
      }
    \LogTblrTracing { target }
  }
\ExplSyntaxOff
\makeatother

\begin{document}

\def\test#1{\par
  Column width = #1, expandable columns with full line width:
  \begin{center}
    \begin{tblr}{colspec = {Q[l,#1]X[c]X[c]},
      width=\linewidth, % 345pt
    	hlines, vlines }
    A1 & B1 & C1  \\
    \end{tblr}
  \end{center}
}

\test{20mm}
\test{56.9055pt}
\end{document}

image

In log

! Package test Error: linewidth: dim 345.0pt, fp 345.
! Package test Error: remaining width: dim 275.6945pt, fp 275.6945.
! Package test Error: remaining width: dim 263.29451pt, fp 263.2945.
! Package test Error: remaining width: dim 250.89452pt, fp 250.8945.

muzimuzhi avatar Jul 06 '23 11:07 muzimuzhi

Thanks for the explanation and the local fix!

Do you consider updating this in the MacTeX distribution?

FloMiLe avatar Jul 07 '23 15:07 FloMiLe

Note fp arithmetic is two orders of magnitude slower than dim arithmetic. Perhaps tabularray wants to introduce a new boolean option like accurate=true|false, in order to change more dim arithmetic to fp.

\documentclass{article}
\usepackage{l3benchmark}

\ExplSyntaxOn
\dim_set:Nn \l_tmpa_dim {100pt}
\fp_set:Nn  \l_tmpa_fp  {100pt}

\benchmark:n { \dim_sub:Nn \l_tmpa_dim {.4pt} } % 4.71e-7 seconds (2 ops)
\benchmark:n { \fp_sub:Nn  \l_tmpa_fp  {.4pt} } % 7.56e-5 seconds (308 ops)
\ExplSyntaxOff

\begin{document}
\end{document}

Do you consider updating this in the MacTeX distribution?

@FloMiLe I'm not the package author nor maintainer so... . According to repo wiki, the next scheduled release date is 2023-09-01 (2023b), see https://github.com/lvjr/tabularray/wiki/ChangeLog#2023-09-01-version-2023b.

muzimuzhi avatar Jul 07 '23 15:07 muzimuzhi

Note fp arithmetic is two orders of magnitude slower than dim arithmetic. Perhaps tabularray wants to introduce a new boolean option like accurate=true|false, in order to change more dim arithmetic to fp.

@muzimuzhi The interface looks good. If you are interested in solving this issue, just do it and commit the change. Same for other issues.

lvjr avatar Jul 08 '23 23:07 lvjr