diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt --- a/libc/src/stdio/printf_core/CMakeLists.txt +++ b/libc/src/stdio/printf_core/CMakeLists.txt @@ -72,6 +72,7 @@ .writer .core_structs libc.src.__support.CPP.limits + libc.src.__support.FPUtil.fputil ) diff --git a/libc/src/stdio/printf_core/float_hex_converter.h b/libc/src/stdio/printf_core/float_hex_converter.h --- a/libc/src/stdio/printf_core/float_hex_converter.h +++ b/libc/src/stdio/printf_core/float_hex_converter.h @@ -9,6 +9,7 @@ #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FLOAT_HEX_CONVERTER_H #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FLOAT_HEX_CONVERTER_H +#include "src/__support/FPUtil/FEnvImpl.h" #include "src/__support/FPUtil/FPBits.h" #include "src/stdio/printf_core/converter_utils.h" #include "src/stdio/printf_core/core_structs.h" @@ -133,11 +134,27 @@ mantissa >>= shift_amount; - // Round to nearest, if it's exactly halfway then round to even. - if (truncated_bits > halfway_const) - ++mantissa; - else if (truncated_bits == halfway_const) - mantissa = mantissa + (mantissa & 1); + switch (fputil::get_round()) { + case FE_TONEAREST: + // Round to nearest, if it's exactly halfway then round to even. + if (truncated_bits > halfway_const) + ++mantissa; + else if (truncated_bits == halfway_const) + mantissa = mantissa + (mantissa & 1); + break; + case FE_DOWNWARD: + if (truncated_bits > 0 && is_negative) { + ++mantissa; + } + break; + case FE_UPWARD: + if (truncated_bits > 0 && !is_negative) { + ++mantissa; + } + break; + case FE_TOWARDZERO: + break; + } // If the rounding caused an overflow, shift the mantissa and adjust the // exponent to match. diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt --- a/libc/test/src/stdio/CMakeLists.txt +++ b/libc/test/src/stdio/CMakeLists.txt @@ -106,6 +106,8 @@ printf_test.cpp DEPENDS libc.src.stdio.printf + libc.src.fenv.fesetround + libc.src.__support.FPUtil.fputil ) add_subdirectory(printf_core) diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp --- a/libc/test/src/stdio/sprintf_test.cpp +++ b/libc/test/src/stdio/sprintf_test.cpp @@ -8,6 +8,7 @@ #include "src/stdio/sprintf.h" +#include "src/__support/FPUtil/FEnvImpl.h" #include "src/__support/FPUtil/FPBits.h" #include "src/__support/FPUtil/PlatformDefs.h" #include "utils/UnitTest/Test.h" @@ -601,6 +602,112 @@ written = __llvm_libc::sprintf(buff, "%5a", -nan); ASSERT_STREQ_LEN(written, buff, " -nan"); + // Rounding Mode Tests. + + __llvm_libc::fputil::set_round(FE_TONEAREST); + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.08p0); + ASSERT_STREQ_LEN(written, buff, "0x1.0p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.18p0); + ASSERT_STREQ_LEN(written, buff, "0x1.2p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.04p0); + ASSERT_STREQ_LEN(written, buff, "0x1.0p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.14p0); + ASSERT_STREQ_LEN(written, buff, "0x1.1p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.08p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.0p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.18p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.2p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.04p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.0p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.14p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.1p+0"); + + __llvm_libc::fputil::set_round(FE_DOWNWARD); + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.08p0); + ASSERT_STREQ_LEN(written, buff, "0x1.0p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.18p0); + ASSERT_STREQ_LEN(written, buff, "0x1.1p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.04p0); + ASSERT_STREQ_LEN(written, buff, "0x1.0p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.14p0); + ASSERT_STREQ_LEN(written, buff, "0x1.1p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.08p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.1p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.18p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.2p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.04p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.1p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.14p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.2p+0"); + + __llvm_libc::fputil::set_round(FE_UPWARD); + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.08p0); + ASSERT_STREQ_LEN(written, buff, "0x1.1p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.18p0); + ASSERT_STREQ_LEN(written, buff, "0x1.2p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.04p0); + ASSERT_STREQ_LEN(written, buff, "0x1.1p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.14p0); + ASSERT_STREQ_LEN(written, buff, "0x1.2p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.08p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.0p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.18p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.1p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.04p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.0p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.14p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.1p+0"); + + __llvm_libc::fputil::set_round(FE_TOWARDZERO); + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.08p0); + ASSERT_STREQ_LEN(written, buff, "0x1.0p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.18p0); + ASSERT_STREQ_LEN(written, buff, "0x1.1p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.04p0); + ASSERT_STREQ_LEN(written, buff, "0x1.0p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", 0x1.14p0); + ASSERT_STREQ_LEN(written, buff, "0x1.1p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.08p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.0p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.18p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.1p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.04p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.0p+0"); + + written = __llvm_libc::sprintf(buff, "%.1a", -0x1.14p0); + ASSERT_STREQ_LEN(written, buff, "-0x1.1p+0"); + + // The remaining tests assume TONEAREST since it's the default on most + // systems. + __llvm_libc::fputil::set_round(FE_TONEAREST); + // Precision Tests. written = __llvm_libc::sprintf(buff, "%.1a", 1.0);