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,25 @@ 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 @@ -11,6 +11,13 @@ #include "src/__support/FPUtil/FPBits.h" #include "src/__support/FPUtil/PlatformDefs.h" #include "utils/UnitTest/Test.h" +#include "utils/testutils/RoundingModeUtils.h" + +class LlvmLibcSPrintfTest : public __llvm_libc::testing::Test { +protected: + char buff[64]; + int written; +}; // Subtract 1 from sizeof(expected_str) to account for the null byte. #define ASSERT_STREQ_LEN(actual_written, actual_str, expected_str) \ @@ -491,9 +498,10 @@ } #ifndef LLVM_LIBC_PRINTF_DISABLE_FLOAT -TEST(LlvmLibcSPrintfTest, FloatHexExpConv) { - char buff[64]; - int written; + +TEST_F(LlvmLibcSPrintfTest, FloatHexExpConv) { + __llvm_libc::testutils::ForceRoundingMode r( + __llvm_libc::testutils::RoundingMode::Nearest); double inf = __llvm_libc::fputil::FPBits::inf().get_val(); double nan = __llvm_libc::fputil::FPBits::build_nan(1); @@ -690,6 +698,124 @@ ASSERT_STREQ_LEN(written, buff, "0x2.0p+16383"); #endif + // Rounding Mode Tests. + + { + __llvm_libc::testutils::ForceRoundingMode r( + __llvm_libc::testutils::RoundingMode::Nearest); + + 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::testutils::ForceRoundingMode r( + __llvm_libc::testutils::RoundingMode::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::testutils::ForceRoundingMode r( + __llvm_libc::testutils::RoundingMode::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::testutils::ForceRoundingMode r( + __llvm_libc::testutils::RoundingMode::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"); + } + // Flag Tests. written = __llvm_libc::sprintf(buff, "%+a", nan);