lvjr / tabularray

Typeset tabulars and arrays with LaTeX3
https://ctan.org/pkg/tabularray
247 stars 22 forks source link

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

Open FloMiLe opened 12 months ago

FloMiLe commented 12 months ago

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?

muzimuzhi commented 12 months ago

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 commented 12 months ago

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

```tex \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.
FloMiLe commented 12 months ago

Thanks for the explanation and the local fix!

Do you consider updating this in the MacTeX distribution?

muzimuzhi commented 12 months ago

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.

lvjr commented 12 months ago

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.