Skip to content

[libc] Enable dyadic float for float printf #110765

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions libc/config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
"value": false,
"doc": "Use large table for better printf long double performance."
},
"LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_DYADIC_FLOAT": {
"value": false,
"doc": "Use dyadic float for faster and smaller but less accurate printf doubles."
},
"LIBC_CONF_PRINTF_FLOAT_TO_STR_NO_SPECIALIZE_LD": {
"value": false,
"doc": "Use the same mode for double and long double in printf."
},
"LIBC_CONF_PRINTF_DISABLE_FIXED_POINT": {
"value": false,
"doc": "Disable printing fixed point values in printf and friends."
Expand Down
2 changes: 2 additions & 0 deletions libc/docs/configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ to learn about the defaults for your platform and target.
- ``LIBC_CONF_PRINTF_DISABLE_INDEX_MODE``: Disable index mode in the printf format string.
- ``LIBC_CONF_PRINTF_DISABLE_STRERROR``: Disable handling of %m to print strerror in printf and friends.
- ``LIBC_CONF_PRINTF_DISABLE_WRITE_INT``: Disable handling of %n in printf format string.
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_NO_SPECIALIZE_LD``: Use the same mode for double and long double in printf.
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_DYADIC_FLOAT``: Use dyadic float for faster and smaller but less accurate printf doubles.
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE``: Use large table for better printf long double performance.
* **"pthread" options**
- ``LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a mutex is in contention (default to 100).
Expand Down
2 changes: 1 addition & 1 deletion libc/src/__support/FPUtil/dyadic_float.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ template <size_t Bits> struct DyadicFloat {
return as<T, /*ShouldSignalExceptions=*/false>();
}

LIBC_INLINE explicit constexpr operator MantissaType() const {
LIBC_INLINE constexpr MantissaType as_mantissa_type() const {
if (mantissa.is_zero())
return 0;

Expand Down
19 changes: 11 additions & 8 deletions libc/src/__support/float_to_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "src/__support/libc_assert.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/sign.h"

// This file has 5 compile-time flags to allow the user to configure the float
// to string behavior. These were used to explore tradeoffs during the design
Expand Down Expand Up @@ -232,7 +233,7 @@ LIBC_INLINE UInt<MID_INT_SIZE> get_table_positive_df(int exponent, size_t i) {
if (shift_amount < 0) {
return 1;
}
fputil::DyadicFloat<INT_SIZE> num(false, 0, 1);
fputil::DyadicFloat<INT_SIZE> num(Sign::POS, 0, 1);
constexpr UInt<INT_SIZE> MOD_SIZE =
(UInt<INT_SIZE>(EXP10_9)
<< (CALC_SHIFT_CONST + (IDX_SIZE > 1 ? IDX_SIZE : 0)));
Expand All @@ -242,16 +243,17 @@ LIBC_INLINE UInt<MID_INT_SIZE> get_table_positive_df(int exponent, size_t i) {
0x89705f4136b4a597}};

static const fputil::DyadicFloat<INT_SIZE> FIVE_EXP_MINUS_NINE(
false, -276, FIVE_EXP_MINUS_NINE_MANT);
Sign::POS, -276, FIVE_EXP_MINUS_NINE_MANT);

if (i > 0) {
fputil::DyadicFloat<INT_SIZE> fives = fputil::pow_n(FIVE_EXP_MINUS_NINE, i);
fputil::DyadicFloat<INT_SIZE> fives =
fputil::pow_n(FIVE_EXP_MINUS_NINE, static_cast<uint32_t>(i));
num = fives;
}
num = mul_pow_2(num, shift_amount);

// Adding one is part of the formula.
UInt<INT_SIZE> int_num = static_cast<UInt<INT_SIZE>>(num) + 1;
UInt<INT_SIZE> int_num = num.as_mantissa_type() + 1;
if (int_num > MOD_SIZE) {
auto rem =
int_num
Expand Down Expand Up @@ -339,23 +341,24 @@ LIBC_INLINE UInt<MID_INT_SIZE> get_table_negative_df(int exponent, size_t i) {

int shift_amount = CALC_SHIFT_CONST - exponent;

fputil::DyadicFloat<INT_SIZE> num(false, 0, 1);
fputil::DyadicFloat<INT_SIZE> num(Sign::POS, 0, 1);
constexpr UInt<INT_SIZE> MOD_SIZE =
(UInt<INT_SIZE>(EXP10_9)
<< (CALC_SHIFT_CONST + (IDX_SIZE > 1 ? IDX_SIZE : 0)));

constexpr UInt<INT_SIZE> TEN_EXP_NINE_MANT(EXP10_9);

static const fputil::DyadicFloat<INT_SIZE> TEN_EXP_NINE(false, 0,
static const fputil::DyadicFloat<INT_SIZE> TEN_EXP_NINE(Sign::POS, 0,
TEN_EXP_NINE_MANT);

if (i > 0) {
fputil::DyadicFloat<INT_SIZE> tens = fputil::pow_n(TEN_EXP_NINE, i);
fputil::DyadicFloat<INT_SIZE> tens =
fputil::pow_n(TEN_EXP_NINE, static_cast<uint32_t>(i));
num = tens;
}
num = mul_pow_2(num, shift_amount);

UInt<INT_SIZE> int_num = static_cast<UInt<INT_SIZE>>(num);
UInt<INT_SIZE> int_num = num.as_mantissa_type();
if (int_num > MOD_SIZE) {
auto rem =
int_num
Expand Down
6 changes: 6 additions & 0 deletions libc/src/stdio/printf_core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ endif()
if(LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE)
list(APPEND printf_config_copts "-DLIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE")
endif()
if(LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_DYADIC_FLOAT)
list(APPEND printf_config_copts "-DLIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT")
endif()
if(LIBC_CONF_PRINTF_FLOAT_TO_STR_NO_SPECIALIZE_LD)
list(APPEND printf_config_copts "-DLIBC_COPT_FLOAT_TO_STR_NO_SPECIALIZE_LD")
endif()
if(LIBC_CONF_PRINTF_DISABLE_FIXED_POINT)
list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_DISABLE_FIXED_POINT")
endif()
Expand Down
3 changes: 3 additions & 0 deletions libc/test/src/stdio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ add_libc_test(
if(LIBC_CONF_PRINTF_DISABLE_FLOAT)
list(APPEND sprintf_test_copts "-DLIBC_COPT_PRINTF_DISABLE_FLOAT")
endif()
if(LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_DYADIC_FLOAT)
list(APPEND sprintf_test_copts "-DLIBC_COPT_FLOAT_TO_STR_REDUCED_PRECISION")
endif()
if(LIBC_CONF_PRINTF_DISABLE_INDEX_MODE)
list(APPEND sprintf_test_copts "-DLIBC_COPT_PRINTF_DISABLE_INDEX_MODE")
endif()
Expand Down
16 changes: 13 additions & 3 deletions libc/test/src/stdlib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,17 @@ add_libc_test(
.strtol_test_support
)

if(LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_DYADIC_FLOAT)
list(APPEND strfrom_test_copts "-DLIBC_COPT_FLOAT_TO_STR_REDUCED_PRECISION")
endif()

add_header_library(
strfrom_test_support
HDRS
StrfromTest.h
StrfromTest.h
DEPENDS
libc.src.__support.CPP.type_traits
libc.src.__support.FPUtil.fp_bits
libc.src.__support.CPP.type_traits
libc.src.__support.FPUtil.fp_bits
)

add_libc_test(
Expand All @@ -186,6 +190,8 @@ add_libc_test(
DEPENDS
.strfrom_test_support
libc.src.stdlib.strfromf
COMPILE_OPTIONS
${strfrom_test_copts}
)

add_libc_test(
Expand All @@ -197,6 +203,8 @@ add_libc_test(
DEPENDS
.strfrom_test_support
libc.src.stdlib.strfromd
COMPILE_OPTIONS
${strfrom_test_copts}
)

add_libc_test(
Expand All @@ -208,6 +216,8 @@ add_libc_test(
DEPENDS
.strfrom_test_support
libc.src.stdlib.strfroml
COMPILE_OPTIONS
${strfrom_test_copts}
)

add_libc_test(
Expand Down
4 changes: 4 additions & 0 deletions libc/test/src/stdlib/StrfromTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ class StrfromTest : public LIBC_NAMESPACE::testing::Test {
written = func(buff, 99, "%f", 1.5);
ASSERT_STREQ_LEN(written, buff, "1.500000");

// Dyadic float is only accurate to ~50 digits, so skip this 300 digit test.
// TODO: Create way to test just the first ~50 digits of a number.
#ifndef LIBC_COPT_FLOAT_TO_STR_REDUCED_PRECISION
written = func(buff, 499, "%f", 1e300);
ASSERT_STREQ_LEN(written, buff,
"100000000000000005250476025520442024870446858110815915491"
Expand All @@ -167,6 +170,7 @@ class StrfromTest : public LIBC_NAMESPACE::testing::Test {
"111903896764088007465274278014249457925878882005684283811"
"566947219638686"
"5459400540160.000000");
#endif // DLIBC_COPT_FLOAT_TO_STR_REDUCED_PRECISION

written = func(buff, 99, "%f", 0.1);
ASSERT_STREQ_LEN(written, buff, "0.100000");
Expand Down
Loading