
\documentclass[12pt]{article}
\usepackage[T2A,OT1]{fontenc}
\usepackage[default]{cantarell}
\usepackage[a4paper, top=20mm, bottom=20mm, left=20mm, right=20mm]{geometry}
\usepackage[utf8]{inputenc}
\usepackage[russian, english]{babel}
\usepackage{tabu}
\usepackage{hyperref}
\usepackage{parskip}
\usepackage{graphicx}
\usepackage{tabularx}
\usepackage[normalem]{ulem}
\usepackage{float}
\floatstyle{boxed}
\restylefloat{figure}
\usepackage{setspace}
\onehalfspacing
\author{Artyom Bologov \href{mailto:printf@aartaka.me}{(email)}}
\date{\today}
\title{Printf Is Useless}
\makeatletter
\def\endenv{\expandafter\end\expandafter{\@currenvir}}
\makeatother
\begin{document}
\maketitle

\includegraphics[width=\textwidth,height=\textheight,keepaspectratio]{./assets/printf.png}

\subsubsection*{A story that actually motivated this post} \label{details} \begin{quote}
I worked on extending a C++ tool.
Making it produce results with higher precision, in particular.
I discovered that it's surprisingly hard to output the custom precision number in C++.
Here are two ways I found:
\begin{figure}[h!]\begin{verbatim}
cout.precision(18);
cout << setprecision(18) << "rest of the output";
\end{verbatim}\caption{Changing the precision of stream output in C++}\end{figure}

Both of these mean modifying the global state of the stream.
Which, in case of \verb|cout|, is quite a consequential change.
Should be avoided at all costs.

The solution my friend (an experienced C++ dev) suggested was to just use \verb|printf|.
But... Is \verb|printf| the C++ way?

Anyway, printing floats is a mess.
Unlike printing anything else.
So this post goes into why you don't need
\verb|printf| and the like...
unless you, like me, is stuck trying to print floats.
\end{quote}

We (programmers) all use formatted output daily.
But most formatting primitives, especially in low-level languages, are for numbers.
So formatted output

\begin{itemize}\item Is un-ergonomic in the simplest case of outputting a single number;
\item  Puts data away from its representation;
\item  And is overcomplicated / unnecessary for non-number output cases.
\end{itemize}

So what I'm trying to say here is not that format strings are useless.
I'm rather of the opinion they can be shortened, optimized, and simplified.
Except for float printing cases where printing parameters actually matter.
The rest is easily accomplished with string interpolation or even separate printing expressions.
No meter-thick historic crust.

\section*{Scheme and SRFI 48 format} \label{scheme-format}

Let's start with something simple.
There were several attempts to standardize format strings in Scheme.
One is
\href{https://srfi.schemers.org/srfi-28/}{SRFI 28 Basic Format Strings},
\href{https://srfi.schemers.org/srfi-48/}{then SRFI 48 Intermediate Format Strings},
\href{https://srfi.schemers.org/srfi-54/}{and SRFI 54 (cat) for alternative take},
and others.

SRFI 28 is quite basic with only three directives:

\begin{description}\item[~a] for human-readable output.
\item[~s] for machine-readable output.
\item[and ~%] for newline.
\end{description}

It's more or less clear why one would want to extend this—while
human-readable vs. machine-readable is quite enough for many cases, one might need finer output.
That's where SRFI 48 comes in:

\begin{figure}[h!]\begin{quote}This SRFI extends SRFI 28 in being more generally useful but is less general than advanced format strings in that it does not allow, <mark>aside from ~F</mark>, for controlled positioning of text within fields.
\end{quote}\caption{Abstract of SRFI 48, highlighting mine}\end{figure}

In other words: SRFI 48 exists for the sole purpose of printing floats prettily.
(I mean, there are lots of other directives, mimicking for C \verb|printf| and Common Lisp \verb|format|.
(Ooops, spoilers!)
But these are still mostly falling into the human/machine-readable category.)
Nothing new in SRFI 48, except float printing.
All the rest is covered by SRFI 28, really.

\section*{printf In C: Exists For Floats} \label{c-printf}

C \verb|printf| is powerful, just enough to
\href{https://github.com/carlini/printf-tac-toe}{power a game of Tic-Tac-Toe}.
It has lots of modifier flags for its directives:

\begin{description}\item[-] Left-justify the printed value.
\item[+] Always show the sign.
\item[space] Pad the value with spaces.
\item[0] Pad the value with zeros.
\item[#] Alternative form.
\end{description}

And there's a moderate number of directives to choose from.
Here's my categorization:

\begin{description}\item[% and n]
Trivial / dangerous.
\item[c and s]
Have dedicated functions (\verb|fputc| and \verb|fputs| respectively) matching them.
\item[p]
Somewhat special, but can also be hexadecimal or whatever integer printing by default.
\item[d/i, u, o, x]
For integers in different bases.
\item[f, e, a, and g]
For floating point numbers.
\end{description}

It's useless, because you have an assortment of number-printing directives.
Some of which you're unlikely to ever use.
And the useful stuff like string printing is so stripped down that it's almost useless too.
How often does C programmer implements conditional printing with
\begin{figure}[h!]\begin{verbatim}
printf("%s%s", (boolean ? "!" : ""), "string");
\end{verbatim}\caption{Conditional printing, C interpretation}\end{figure}

Too often.
String interpolation akin to JavaScript would make more sense.
It would both put the data into the output.
And won't bother with the obvious (from the type system point of view) details of printing directives.
\begin{figure}[h!]\begin{verbatim}
console.log(`${boolean ? "!" : ""}string`)
\end{verbatim}\caption{JavaScript interpolated version }\end{figure}

While this example would require type inference to be ported to C, it's a good things to aim for.
(And you'll see that it's achievable in the end of this post.)

\section*{Counterexample: Common Lisp format} \label{cl-format}

Common Lisp's
\verb|format| is likely Turing-complete.
Not because of some \verb|%n| hack that makes \verb|printf| dangerous.
But rather because it was intended as a one-stop shop for output.

I'll refer you to
\pageref{format}{my earlier post} for a full feature listing (and horrible format string from my own experience!)
But the gist is that \verb|format| is a control flow of its own, replacing conditionals and loops by format strings.
And that makes it more useful, but the problem of increasing formatting-data distance is still there.

\section*{Solution: Generic Printing and Interpolation} \label{solution}

I'll handle the hardest case out of these three: C data formatting.
Thanks to C11 generics, it's possible to implement generic printing
(and that's exactly what I'm doing in my
\href{https://github.com/aartaka/pretty.c}{Pretty.C)}
that covers 99% of the printing cases:
\begin{figure}[h!]\begin{verbatim}
#if __STDC_VERSION__ >= 201112L
#define print(...)                                        \
        _Generic((__VA_ARGS__),                                \
                 _Bool: fputs(stdout, (_Bool)(__VA_ARGS__) ? "true" : "false"), \
                 default: printf(                              \
                         _Generic((__VA_ARGS__),               \
                                  char*:              "%s",    \
                                  char:               "%c",    \
                                  signed char:        "%hhi",  \
                                  short:              "%hi",   \
                                  int:                "%i",    \
                                  long:               "%li",   \
                                  long long:          "%lli",  \
                                  unsigned char:      "%hhu",  \
                                  unsigned short:     "%hi",   \
                                  unsigned int:       "%u",    \
                                  unsigned long:      "%lu",   \
                                  unsigned long long: "%llu",  \
                                  float:              "%g",    \
                                  double:             "%g",    \
                                  long double:        "%Lg",   \
                                  default:            "%p"),   \
                         (__VA_ARGS__)))
#endif
\end{verbatim}\caption{Generic printing in C}\end{figure}

If there is \verb|tgmath.h| in C, why is there no \verb|tgio.h|?
I dunno, maybe I should file a proposal to C standard people?

Here's how the example from earlier looks with this new macro:
\begin{figure}[h!]\begin{verbatim}
if (boolean)
        print("!");
print("string");
\end{verbatim}\caption{Problematic example fixed with newly defined print() macro}\end{figure}

It's the same length as printf (or even less if we don't consider 8 spaces of indentation!)
But the structure and syntax are easier on both human and machine readers.

The solution in Scheme and CL
\href{http://cs-www.cs.yale.edu/homes/dvm/format-stinks.html}{was already proposed}:
just use language macro facilities to implement printing that actually reflect the output shape.
Something like string interpolation, but even more structured and enforceable!

So yes, you likely don't need
\verb|printf|, you need structured and simple output.
Like string interpolation or generic printing.
You're still stuck with
\verb|printf| if you're on C compilers nostalgic for the times before C11.
But, that aside, use some structured output instead of formatted output, won't you?


\par\noindent\rule{\textwidth}{0.4pt}
\href{https://creativecommons.org/licenses/by/4.0}{CC-BY 4.0} 2022-2026 by Artyom Bologov (aartaka,)
\href{https://codeberg.org/aartaka/pages/commit/a91befa}{with one commit remixing Claude-generated code}.
Any and all opinions listed here are my own and not representative of my employers; future, past and present.
\end{document}
