What follows assumes that your man & friends come from man-db. Considering that Pop!_OS is based on Ubuntu and that Ubuntu 20.04 has man-db installed by default, this should be true.
When operating in "whatis" or in "apropos" mode (man -f and man -k, respectively), man actually invokes the whatis binary and delegates the search to it. On the other hand, when operating in its default mode (e.g. man page) or in "where" mode (man -w page), the search is performed by the man binary itself. The search routines of man and whatis are implemented independently of each other.
Two main reasons may make whatis list fewer manual pages than man -a:
incomplete configuration: if the search path configured in /etc/manpath.config (used on Debian and derivatives; /etc/man_db.conf on some other distributions) includes all the directories containing manual pages on your system but there is no MANDB_MAP for some of them in that file, then an index database is not initialized (nor searched, if existing) for them; man -a will find the pages they contain anyway, because it directly searches the directories listed in the search path, while whatis won't, because it only searches the index database;
even when all the directories containing manual pages have proper MANDATORY_MANPATH and MANDB_MAP entries in /etc/manpath.config, whatis may still list fewer results than man -a because it simply omits duplicate name-section combinations from its output. In your case, printf(1) is found in both /usr/share/man and /usr/share/fish/man and only the one from the directory that comes first in the search path is listed.
You can use the manpath command to display the search path that man & friends will use, and make sure it includes all the relevant directories. By default it is built based on the MANDATORY_MANPATH entries in /etc/manpath.config.
man & friends can also be invoked with an explicitly defined search path by setting MANPATH to alter the search results. For instance, if you are in case (2),
MANPATH=/usr/share/fish/man:/usr/share/man whatis printf
will likely print the one-line description for /usr/share/fish/man/man1/printf.1 and not the one for /usr/share/man/man1/printf.1.gz.
You can also look at what is going on under the hood by using the --debug option. It will likely show that your programs are using a search path that lists /usr/share/man before /usr/share/fish/man and that all three files are found, but a line for the second found printf in section 1 is just not printed by whatis (or man -f).
Finally, to address case (2), you may define a helper function that wraps man to make it list duplicate name-section combinations too:
slowman () (
IFS=:
for path in ${MANPATH-$(manpath)}
do
printf '%s\n' "Searching ${path}:" 1>&2
MANPATH="$path" man "$@"
done
)