Temporarily changing rounding modes is not that unusual and it is possible with bin/printf
although not per se you need to change the sources.
You need the sources of the coreutils, I used the latest version available today which was http://ftp.gnu.org/gnu/coreutils/coreutils-8.24.tar.xz.
Unpack into a directory of your choice with
tar xJfv coreutils-8.24.tar.xz
Change into the source-directory
cd coreutils-8.24
Load the file src/printf.c
into the editor of your choice and exchange the whole main
function with the following function including both of the preprocessor directives to include the header files math.h
and fenv.h
. The main function is at the end and starts at int main...
and ends at the very end of the file with the closing bracket }
#include <math.h>
#include <fenv.h>
int
main (int argc, char **argv)
{
char *format;
char *rounding_env;
int args_used;
int rounding_mode;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
exit_status = EXIT_SUCCESS;
posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
// accept rounding modes from an environment variable
if ((rounding_env = getenv ("BIN_PRINTF_ROUNDING_MODE")) != NULL)
{
rounding_mode = atoi(rounding_env);
switch (rounding_mode)
{
case 0:
if (fesetround(FE_TOWARDZERO) != 0)
{
error (0, 0, _("setting rounding mode to roundTowardZero failed"));
return EXIT_FAILURE;
}
break;
case 1:
if (fesetround(FE_TONEAREST) != 0)
{
error (0, 0, _("setting rounding mode to roundTiesToEven failed"));
return EXIT_FAILURE;
}
break;
case 2:
if (fesetround(FE_UPWARD) != 0)
{
error (0, 0, _("setting rounding mode to roundTowardPositive failed"));
return EXIT_FAILURE;
}
break;
case 3:
if (fesetround(FE_DOWNWARD) != 0)
{
error (0, 0, _("setting rounding mode to roundTowardNegative failed"));
return EXIT_FAILURE;
}
break;
default:
error (0, 0, _("setting rounding mode failed for unknown reason"));
return EXIT_FAILURE;
}
}
/* We directly parse options, rather than use parse_long_options, in
order to avoid accepting abbreviations. */
if (argc == 2)
{
if (STREQ (argv[1], "--help"))
usage (EXIT_SUCCESS);
if (STREQ (argv[1], "--version"))
{
version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,
(char *) NULL);
return EXIT_SUCCESS;
}
}
/* The above handles --help and --version.
Since there is no other invocation of getopt, handle '--' here. */
if (1 < argc && STREQ (argv[1], "--"))
{
--argc;
++argv;
}
if (argc <= 1)
{
error (0, 0, _("missing operand"));
usage (EXIT_FAILURE);
}
format = argv[1];
argc -= 2;
argv += 2;
do
{
args_used = print_formatted (format, argc, argv);
argc -= args_used;
argv += args_used;
}
while (args_used > 0 && argc > 0);
if (argc > 0)
error (0, 0,
_("warning: ignoring excess arguments, starting with %s"),
quote (argv[0]));
return exit_status;
}
Run ./configure
as follows
LIBS=-lm ./configure --program-suffix=-own
It puts the suffix -own
at every subprogram (there are a lot) just in case you want to install them all and are unsure if they fit with the rest of the system. The coreutils are not named coreutils without a reason!
But the most important is the LIBS=-lm
in front of the line. We need the mathematical library and this command tells ./configure
to add it to the list of needed libraries.
Run make
make
If you have a multicore/multiprocessor system try
make -j4
where the number (here "4") should represent the number of cores you are willing to spare for that job.
If all went well you have the new printf
int src/printf
. Try it out:
BIN_PRINTF_ROUNDING_MODE=1 ./src/printf '%.0f\n' 196.5
BIN_PRINTF_ROUNDING_MODE=2 ./src/printf '%.0f\n' 196.5
Both command should differ in output. The numbers after IN_PRINTF_ROUNDING_MODE
mean:
- 0 Rounding toward 0
- 1 Rounding toward nearest number (default)
- 2 Rounding toward positive infinity
- 3 Rounding toward negative infinity
You may install the whole (not recommended) or just copy the file (renaming it before is highly recommended!) src/printf
into a directory in your PATH
and use as described above.