[libc] Add Q length modifier to support float128 conversions in printf (#203077)
The spelling follows an existing pattern in e.g. libquadmath for format printing `__float128` values. A flag is added to disable float128 conversions and the "Q" length modifier.
This commit is contained in:
1 parent
5e26548403
commit
03c62ca40d
15 files changed
+614
-153
No files matched your search
@@ -105,6 +105,13 @@ with hexadecimal long double conversions (%La). This will improve performance
|
||||
significantly, but may cause some tests to fail. This has no effect when float
|
||||
conversions are disabled.
|
||||
|
||||
LIBC_COPT_PRINTF_NO_CONVERT_FLOAT128
|
||||
------------------------------------
|
||||
When set, this flag disables support for __float128 conversions using the "Q"
|
||||
length modifier (%Qa, %Qf, %Qe, %Qg). This flag has no effect on conversions
|
||||
using the "L" length modifier when long double is the same type as __float128.
|
||||
This has little to no effect on performance or binary size.
|
||||
|
||||
--------------------------------
|
||||
Float Conversion Internal Flags:
|
||||
--------------------------------
|
||||
@@ -129,7 +136,7 @@ LIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE
|
||||
-------------------------------------------------
|
||||
When set, the float to string decimal conversion algorithm will use a larger
|
||||
table to accelerate long double conversions. This larger table is around 5MB of
|
||||
size when compiled.
|
||||
size when compiled. This flag also affects __float128 conversions.
|
||||
|
||||
LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT
|
||||
---------------------------------------
|
||||
@@ -244,3 +251,10 @@ value of errno as an integer with the %d format, including all options. If
|
||||
errno = 0 and alt form is specified, the conversion will be a string conversion
|
||||
on "0" for simplicity of implementation. This matches what other libcs
|
||||
implementing this feature have done.
|
||||
|
||||
If the compiler is detected as having support for __float128, "Q" is an accepted
|
||||
length modifier for floating point conversions (%Qa, %Qf, %Qe, %Qg), unless
|
||||
disabled by LIBC_COPT_PRINTF_NO_CONVERT_FLOAT128. A conversion using the
|
||||
"Q" length modifier will be treated as invalid in any of the following
|
||||
conditions: __float128 is not supported, the "Q" length modifier is disabled, or
|
||||
the conversion does not use a floating point format specifier.
|
||||
@@ -385,6 +385,14 @@ LIBC_INLINE uint32_t mul_shift_mod_1e9(const FPBits::StorageType mantissa,
|
||||
|
||||
} // namespace internal
|
||||
|
||||
#if defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64) || \
|
||||
defined(LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE) || \
|
||||
defined(LIBC_COPT_FLOAT_TO_STR_NO_SPECIALIZE_LD)
|
||||
#define LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL
|
||||
#endif
|
||||
|
||||
template <typename T, typename U = void> class FloatToString;
|
||||
|
||||
// Convert floating point values to their string representation.
|
||||
// Because the result may not fit in a reasonably sized array, the caller must
|
||||
// request blocks of digits and convert them from integers to strings themself.
|
||||
@@ -401,8 +409,13 @@ LIBC_INLINE uint32_t mul_shift_mod_1e9(const FPBits::StorageType mantissa,
|
||||
// be zero after the decimal point and before the non-zero digits. Additionally,
|
||||
// is_lowest_block will return if the current block is the lowest non-zero
|
||||
// block.
|
||||
template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
|
||||
class FloatToString {
|
||||
template <typename T>
|
||||
class FloatToString<
|
||||
T, cpp::enable_if_t<cpp::is_same_v<T, float> || cpp::is_same_v<T, double>
|
||||
#if defined(LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL)
|
||||
|| cpp::is_same_v<T, long double>
|
||||
#endif // LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL
|
||||
>> {
|
||||
fputil::FPBits<T> float_bits;
|
||||
int exponent;
|
||||
FPBits::StorageType mantissa;
|
||||
@@ -605,36 +618,46 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
#if !defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64) && \
|
||||
!defined(LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE) && \
|
||||
!defined(LIBC_COPT_FLOAT_TO_STR_NO_SPECIALIZE_LD)
|
||||
// --------------------------- LONG DOUBLE FUNCTIONS ---------------------------
|
||||
#if !defined(LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL) || \
|
||||
defined(LIBC_TYPES_HAS_FLOAT128)
|
||||
// -------------------- LONG DOUBLE / FLOAT128 FUNCTIONS -----------------------
|
||||
|
||||
// this algorithm will work exactly the same for 80 bit and 128 bit long
|
||||
// This algorithm will work exactly the same for 80 bit and 128 bit long
|
||||
// doubles. They have the same max exponent, but even if they didn't the
|
||||
// constants should be calculated to be correct for any provided floating point
|
||||
// type.
|
||||
|
||||
template <> class FloatToString<long double> {
|
||||
fputil::FPBits<long double> float_bits;
|
||||
#if defined(LIBC_TYPES_HAS_FLOAT128)
|
||||
template <typename T> struct IsFloat128 : cpp::is_same<T, float128> {};
|
||||
#else
|
||||
template <typename T> struct IsFloat128 : cpp::bool_constant<false> {};
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
class FloatToString<T, cpp::enable_if_t<IsFloat128<T>::value
|
||||
#if defined(LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL)
|
||||
&& !cpp::is_same_v<T, long double>
|
||||
#else
|
||||
|| cpp::is_same_v<T, long double>
|
||||
#endif // LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL
|
||||
>> {
|
||||
fputil::FPBits<T> float_bits;
|
||||
bool is_negative = 0;
|
||||
int exponent = 0;
|
||||
fputil::FPBits<long double>::StorageType mantissa = 0;
|
||||
typename fputil::FPBits<T>::StorageType mantissa = 0;
|
||||
|
||||
static constexpr int FRACTION_LEN = fputil::FPBits<long double>::FRACTION_LEN;
|
||||
static constexpr int EXP_BIAS = fputil::FPBits<long double>::EXP_BIAS;
|
||||
static constexpr int FRACTION_LEN = fputil::FPBits<T>::FRACTION_LEN;
|
||||
static constexpr int EXP_BIAS = fputil::FPBits<T>::EXP_BIAS;
|
||||
static constexpr size_t UINT_WORD_SIZE = 64;
|
||||
|
||||
static constexpr size_t FLOAT_AS_INT_WIDTH =
|
||||
internal::div_ceil(
|
||||
fputil::FPBits<long double>::MAX_BIASED_EXPONENT -
|
||||
fputil::FPBits<long double>::EXP_BIAS +
|
||||
fputil::FPBits<T>::MAX_BIASED_EXPONENT - fputil::FPBits<T>::EXP_BIAS +
|
||||
FRACTION_LEN, // Add fraction len to provide space for subnormals.
|
||||
UINT_WORD_SIZE) *
|
||||
UINT_WORD_SIZE;
|
||||
static constexpr size_t EXTRA_INT_WIDTH =
|
||||
internal::div_ceil(sizeof(long double) * CHAR_BIT, UINT_WORD_SIZE) *
|
||||
UINT_WORD_SIZE;
|
||||
internal::div_ceil(sizeof(T) * CHAR_BIT, UINT_WORD_SIZE) * UINT_WORD_SIZE;
|
||||
|
||||
using wide_int = UInt<FLOAT_AS_INT_WIDTH + EXTRA_INT_WIDTH>;
|
||||
|
||||
@@ -705,7 +728,7 @@ template <> class FloatToString<long double> {
|
||||
// if the shift amount would be negative, then the shift would cause a
|
||||
// loss of precision.
|
||||
LIBC_ASSERT(SHIFT_AMOUNT >= 0);
|
||||
static_assert(EXTRA_INT_WIDTH >= sizeof(long double) * 8);
|
||||
static_assert(EXTRA_INT_WIDTH >= sizeof(T) * 8);
|
||||
float_as_fixed <<= SHIFT_AMOUNT;
|
||||
|
||||
// If there are still digits above the decimal point, handle those.
|
||||
@@ -730,8 +753,7 @@ template <> class FloatToString<long double> {
|
||||
}
|
||||
|
||||
public:
|
||||
LIBC_INLINE constexpr FloatToString(long double init_float)
|
||||
: float_bits(init_float) {
|
||||
LIBC_INLINE constexpr FloatToString(T init_float) : float_bits(init_float) {
|
||||
is_negative = float_bits.is_neg();
|
||||
exponent = float_bits.get_explicit_exponent();
|
||||
mantissa = float_bits.get_explicit_mantissa();
|
||||
@@ -834,8 +856,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
#endif // !LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64 &&
|
||||
// !LIBC_COPT_FLOAT_TO_STR_NO_SPECIALIZE_LD
|
||||
#endif // !LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL || LIBC_TYPES_HAS_FLOAT128
|
||||
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
|
||||
|
||||
@@ -57,6 +57,10 @@ LIBC_INLINE uintmax_t apply_length_modifier(uintmax_t num,
|
||||
return num & mask;
|
||||
}
|
||||
#endif // LIBC_COPT_PRINTF_DISABLE_BITINT
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
case LengthModifier::Q: // This case should never happen for integers.
|
||||
return num;
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,9 @@ enum class LengthModifier {
|
||||
z,
|
||||
t,
|
||||
L,
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
Q,
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
#ifndef LIBC_COPT_PRINTF_DISABLE_BITINT
|
||||
w,
|
||||
wf,
|
||||
@@ -45,6 +48,12 @@ struct LengthSpec {
|
||||
size_t bit_width;
|
||||
};
|
||||
|
||||
// Type large enough to store the raw bits of any floating point type.
|
||||
//
|
||||
// Does not use any specialization of FPBits, because it is unavailable on
|
||||
// PowerPC.
|
||||
using AnyFloatStorageType = UInt128;
|
||||
|
||||
enum FormatFlags : uint8_t {
|
||||
LEFT_JUSTIFIED = 0x01, // -
|
||||
FORCE_SIGN = 0x02, // +
|
||||
@@ -69,13 +78,7 @@ struct FormatSection {
|
||||
int min_width = 0;
|
||||
int precision = -1;
|
||||
|
||||
// Needs to be large enough to hold a long double. Special case handling for
|
||||
// the PowerPC double double type because it has no FPBits interface.
|
||||
#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
UInt128 conv_val_raw;
|
||||
#else
|
||||
fputil::FPBits<long double>::StorageType conv_val_raw;
|
||||
#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
AnyFloatStorageType conv_val_raw;
|
||||
void *conv_val_ptr;
|
||||
|
||||
char conv_name;
|
||||
|
||||
@@ -609,16 +609,9 @@ template <typename T, WriteMode write_mode,
|
||||
LIBC_INLINE int convert_float_dec_exp_typed(Writer<write_mode> *writer,
|
||||
const FormatSection &to_conv,
|
||||
fputil::FPBits<T> float_bits) {
|
||||
#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
using StorageType = UInt128;
|
||||
#else
|
||||
using StorageType = fputil::FPBits<long double>::StorageType;
|
||||
#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
|
||||
// signed because later we use -FRACTION_LEN
|
||||
constexpr int32_t FRACTION_LEN = fputil::FPBits<T>::FRACTION_LEN;
|
||||
int exponent = float_bits.get_explicit_exponent();
|
||||
StorageType mantissa = float_bits.get_explicit_mantissa();
|
||||
|
||||
char sign_char = 0;
|
||||
|
||||
@@ -657,7 +650,7 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer<write_mode> *writer,
|
||||
|
||||
// If the mantissa is 0, then the number is 0, meaning that looping until a
|
||||
// non-zero block is found will loop forever. The first block is just 0.
|
||||
if (mantissa != 0) {
|
||||
if (float_bits.get_explicit_mantissa() != 0) {
|
||||
// This loop finds the first block.
|
||||
while (digits == 0) {
|
||||
--cur_block;
|
||||
@@ -777,16 +770,9 @@ template <typename T, WriteMode write_mode,
|
||||
LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
|
||||
const FormatSection &to_conv,
|
||||
fputil::FPBits<T> float_bits) {
|
||||
#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
using StorageType = UInt128;
|
||||
#else
|
||||
using StorageType = fputil::FPBits<long double>::StorageType;
|
||||
#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
|
||||
// signed because later we use -FRACTION_LEN
|
||||
constexpr int32_t FRACTION_LEN = fputil::FPBits<T>::FRACTION_LEN;
|
||||
int exponent = float_bits.get_explicit_exponent();
|
||||
StorageType mantissa = float_bits.get_explicit_mantissa();
|
||||
|
||||
// From the standard: Let P (init_precision) equal the precision if nonzero, 6
|
||||
// if the precision is omitted, or 1 if the precision is zero.
|
||||
@@ -821,7 +807,7 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
|
||||
|
||||
// If the mantissa is 0, then the number is 0, meaning that looping until a
|
||||
// non-zero block is found will loop forever.
|
||||
if (mantissa != 0) {
|
||||
if (float_bits.get_explicit_mantissa() != 0) {
|
||||
// This loop finds the first non-zero block.
|
||||
while (digits == 0) {
|
||||
--cur_block;
|
||||
@@ -1150,9 +1136,20 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
|
||||
template <WriteMode write_mode>
|
||||
LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
|
||||
const FormatSection &to_conv) {
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
if (to_conv.length_modifier == LengthModifier::Q) {
|
||||
fputil::FPBits<float128>::StorageType float_raw = to_conv.conv_val_raw;
|
||||
fputil::FPBits<float128> float_bits(float_raw);
|
||||
if (!float_bits.is_inf_or_nan()) {
|
||||
return convert_float_decimal_typed<float128>(writer, to_conv, float_bits);
|
||||
}
|
||||
} else
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
if (to_conv.length_modifier == LengthModifier::L) {
|
||||
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
|
||||
if (to_conv.length_modifier == LengthModifier::L) {
|
||||
fputil::FPBits<long double>::StorageType float_raw =
|
||||
static_cast<fputil::FPBits<long double>::StorageType>(
|
||||
to_conv.conv_val_raw);
|
||||
fputil::FPBits<long double> float_bits(float_raw);
|
||||
if (!float_bits.is_inf_or_nan()) {
|
||||
return convert_float_decimal_typed<long double>(writer, to_conv,
|
||||
@@ -1175,9 +1172,20 @@ LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
|
||||
template <WriteMode write_mode>
|
||||
LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer,
|
||||
const FormatSection &to_conv) {
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
if (to_conv.length_modifier == LengthModifier::Q) {
|
||||
fputil::FPBits<float128>::StorageType float_raw = to_conv.conv_val_raw;
|
||||
fputil::FPBits<float128> float_bits(float_raw);
|
||||
if (!float_bits.is_inf_or_nan()) {
|
||||
return convert_float_dec_exp_typed<float128>(writer, to_conv, float_bits);
|
||||
}
|
||||
} else
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
if (to_conv.length_modifier == LengthModifier::L) {
|
||||
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
|
||||
if (to_conv.length_modifier == LengthModifier::L) {
|
||||
fputil::FPBits<long double>::StorageType float_raw =
|
||||
static_cast<fputil::FPBits<long double>::StorageType>(
|
||||
to_conv.conv_val_raw);
|
||||
fputil::FPBits<long double> float_bits(float_raw);
|
||||
if (!float_bits.is_inf_or_nan()) {
|
||||
return convert_float_dec_exp_typed<long double>(writer, to_conv,
|
||||
@@ -1200,9 +1208,21 @@ LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer,
|
||||
template <WriteMode write_mode>
|
||||
LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
|
||||
const FormatSection &to_conv) {
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
if (to_conv.length_modifier == LengthModifier::Q) {
|
||||
fputil::FPBits<float128>::StorageType float_raw = to_conv.conv_val_raw;
|
||||
fputil::FPBits<float128> float_bits(float_raw);
|
||||
if (!float_bits.is_inf_or_nan()) {
|
||||
return convert_float_dec_auto_typed<float128>(writer, to_conv,
|
||||
float_bits);
|
||||
}
|
||||
} else
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
if (to_conv.length_modifier == LengthModifier::L) {
|
||||
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
|
||||
if (to_conv.length_modifier == LengthModifier::L) {
|
||||
fputil::FPBits<long double>::StorageType float_raw =
|
||||
static_cast<fputil::FPBits<long double>::StorageType>(
|
||||
to_conv.conv_val_raw);
|
||||
fputil::FPBits<long double> float_bits(float_raw);
|
||||
if (!float_bits.is_inf_or_nan()) {
|
||||
return convert_float_dec_auto_typed<long double>(writer, to_conv,
|
||||
|
||||
@@ -78,7 +78,8 @@ struct DigitsInput {
|
||||
// Constructor which accepts a mantissa direct from a floating-point format,
|
||||
// and shifts it up to the top of the UInt128 so that a function consuming
|
||||
// this struct afterwards doesn't have to remember which format it came from.
|
||||
DigitsInput(int32_t fraction_len, UInt128 mantissa_, int exponent_, Sign sign)
|
||||
DigitsInput(int32_t fraction_len, AnyFloatStorageType mantissa_,
|
||||
int exponent_, Sign sign)
|
||||
: mantissa(UInt128(mantissa_) << (127 - fraction_len)),
|
||||
exponent(exponent_), sign(sign) {
|
||||
if (!(mantissa & (UInt128(1) << 127)) && mantissa != 0) {
|
||||
@@ -374,10 +375,11 @@ DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
|
||||
}
|
||||
|
||||
template <WriteMode write_mode>
|
||||
LIBC_INLINE int
|
||||
convert_float_inner(Writer<write_mode> *writer, const FormatSection &to_conv,
|
||||
int32_t fraction_len, int exponent, UInt128 mantissa,
|
||||
Sign sign, ConversionType ctype) {
|
||||
LIBC_INLINE int convert_float_inner(Writer<write_mode> *writer,
|
||||
const FormatSection &to_conv,
|
||||
int32_t fraction_len, int exponent,
|
||||
AnyFloatStorageType mantissa, Sign sign,
|
||||
ConversionType ctype) {
|
||||
constexpr char DECIMAL_POINT = '.';
|
||||
// If to_conv doesn't specify a precision, the precision defaults to 6.
|
||||
unsigned precision = to_conv.precision < 0 ? 6 : to_conv.precision;
|
||||
@@ -631,10 +633,21 @@ template <WriteMode write_mode>
|
||||
LIBC_INLINE int convert_float_outer(Writer<write_mode> *writer,
|
||||
const FormatSection &to_conv,
|
||||
ConversionType ctype) {
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
if (to_conv.length_modifier == LengthModifier::Q) {
|
||||
fputil::FPBits<float128> float_bits(
|
||||
static_cast<fputil::FPBits<float128>::StorageType>(
|
||||
to_conv.conv_val_raw));
|
||||
if (!float_bits.is_inf_or_nan()) {
|
||||
return convert_float_typed<float128>(writer, to_conv, float_bits, ctype);
|
||||
}
|
||||
} else
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
if (to_conv.length_modifier == LengthModifier::L) {
|
||||
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
|
||||
fputil::FPBits<long double> float_bits(float_raw);
|
||||
if (to_conv.length_modifier == LengthModifier::L) {
|
||||
fputil::FPBits<long double> float_bits(
|
||||
static_cast<fputil::FPBits<long double>::StorageType>(
|
||||
to_conv.conv_val_raw));
|
||||
if (!float_bits.is_inf_or_nan()) {
|
||||
return convert_float_typed<long double>(writer, to_conv, float_bits,
|
||||
ctype);
|
||||
|
||||
@@ -25,52 +25,72 @@
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
namespace printf_core {
|
||||
|
||||
struct FloatHexExpFPBitsProperties {
|
||||
bool is_negative;
|
||||
int exponent;
|
||||
AnyFloatStorageType mantissa;
|
||||
bool is_inf_or_nan;
|
||||
uint32_t fraction_bits;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
FloatHexExpFPBitsProperties
|
||||
get_float_hex_exp_fp_bits_properties(AnyFloatStorageType float_raw) {
|
||||
fputil::FPBits<T> float_bits(
|
||||
static_cast<typename fputil::FPBits<T>::StorageType>(float_raw));
|
||||
return {
|
||||
.is_negative = float_bits.is_neg(),
|
||||
.exponent = float_bits.get_explicit_exponent(),
|
||||
.mantissa = float_bits.get_explicit_mantissa(),
|
||||
.is_inf_or_nan = float_bits.is_inf_or_nan(),
|
||||
.fraction_bits = fputil::FPBits<T>::FRACTION_LEN,
|
||||
};
|
||||
}
|
||||
|
||||
template <WriteMode write_mode>
|
||||
LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
|
||||
const FormatSection &to_conv) {
|
||||
#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
using LDBits = fputil::FPBits<double>;
|
||||
using StorageType = LDBits::StorageType;
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
static constexpr uint32_t MAX_POSSIBLE_FRACTION_LEN =
|
||||
fputil::FPBits<float128>::FRACTION_LEN;
|
||||
static constexpr uint32_t MAX_POSSIBLE_EXP_LEN =
|
||||
fputil::FPBits<float128>::EXP_LEN;
|
||||
#elif !defined(LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE)
|
||||
static constexpr uint32_t MAX_POSSIBLE_FRACTION_LEN =
|
||||
fputil::FPBits<long double>::FRACTION_LEN;
|
||||
static constexpr uint32_t MAX_POSSIBLE_EXP_LEN =
|
||||
fputil::FPBits<long double>::EXP_LEN;
|
||||
#else
|
||||
using LDBits = fputil::FPBits<long double>;
|
||||
using StorageType = LDBits::StorageType;
|
||||
#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
|
||||
bool is_negative;
|
||||
int exponent;
|
||||
StorageType mantissa;
|
||||
bool is_inf_or_nan;
|
||||
uint32_t fraction_bits;
|
||||
static constexpr uint32_t MAX_POSSIBLE_FRACTION_LEN =
|
||||
fputil::FPBits<double>::FRACTION_LEN;
|
||||
static constexpr uint32_t MAX_POSSIBLE_EXP_LEN =
|
||||
fputil::FPBits<double>::EXP_LEN;
|
||||
#endif
|
||||
|
||||
FloatHexExpFPBitsProperties properties;
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
if (to_conv.length_modifier == LengthModifier::Q) {
|
||||
properties =
|
||||
get_float_hex_exp_fp_bits_properties<float128>(to_conv.conv_val_raw);
|
||||
} else
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
if (to_conv.length_modifier == LengthModifier::L) {
|
||||
fraction_bits = LDBits::FRACTION_LEN;
|
||||
LDBits::StorageType float_raw = to_conv.conv_val_raw;
|
||||
LDBits float_bits(float_raw);
|
||||
is_negative = float_bits.is_neg();
|
||||
exponent = float_bits.get_explicit_exponent();
|
||||
mantissa = float_bits.get_explicit_mantissa();
|
||||
is_inf_or_nan = float_bits.is_inf_or_nan();
|
||||
if (to_conv.length_modifier == LengthModifier::L) {
|
||||
properties =
|
||||
get_float_hex_exp_fp_bits_properties<long double>(to_conv.conv_val_raw);
|
||||
} else
|
||||
#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
{
|
||||
using LBits = fputil::FPBits<double>;
|
||||
fraction_bits = LBits::FRACTION_LEN;
|
||||
LBits::StorageType float_raw =
|
||||
static_cast<LBits::StorageType>(to_conv.conv_val_raw);
|
||||
LBits float_bits(float_raw);
|
||||
is_negative = float_bits.is_neg();
|
||||
exponent = float_bits.get_explicit_exponent();
|
||||
mantissa = float_bits.get_explicit_mantissa();
|
||||
is_inf_or_nan = float_bits.is_inf_or_nan();
|
||||
properties =
|
||||
get_float_hex_exp_fp_bits_properties<double>(to_conv.conv_val_raw);
|
||||
}
|
||||
|
||||
if (is_inf_or_nan)
|
||||
if (properties.is_inf_or_nan)
|
||||
return convert_inf_nan(writer, to_conv);
|
||||
|
||||
char sign_char = 0;
|
||||
|
||||
if (is_negative)
|
||||
if (properties.is_negative)
|
||||
sign_char = '-';
|
||||
else if ((to_conv.flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
|
||||
sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
|
||||
@@ -84,8 +104,9 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
|
||||
// digits. This is primarily relevant for x86 80 bit long doubles, which have
|
||||
// 63 bit mantissas. In the case where the mantissa is 0, however, the
|
||||
// exponent should stay as 0.
|
||||
if (fraction_bits % BITS_IN_HEX_DIGIT != 0 && mantissa > 0) {
|
||||
exponent -= fraction_bits % BITS_IN_HEX_DIGIT;
|
||||
if (properties.fraction_bits % BITS_IN_HEX_DIGIT != 0 &&
|
||||
properties.mantissa > 0) {
|
||||
properties.exponent -= properties.fraction_bits % BITS_IN_HEX_DIGIT;
|
||||
}
|
||||
|
||||
// This is the max number of digits it can take to represent the mantissa.
|
||||
@@ -93,10 +114,10 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
|
||||
// for the extra implicit bit. We use the larger of the two possible values
|
||||
// since the size must be constant.
|
||||
constexpr size_t MANT_BUFF_LEN =
|
||||
(LDBits::FRACTION_LEN / BITS_IN_HEX_DIGIT) + 1;
|
||||
(MAX_POSSIBLE_FRACTION_LEN / BITS_IN_HEX_DIGIT) + 1;
|
||||
char mant_buffer[MANT_BUFF_LEN];
|
||||
|
||||
size_t mant_len = (fraction_bits / BITS_IN_HEX_DIGIT) + 1;
|
||||
size_t mant_len = (properties.fraction_bits / BITS_IN_HEX_DIGIT) + 1;
|
||||
|
||||
// Precision only tracks the number of digits after the hexadecimal point, so
|
||||
// we have to add one to account for the digit before the hexadecimal point.
|
||||
@@ -106,11 +127,12 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
|
||||
const size_t shift_amount =
|
||||
(mant_len - intended_digits) * BITS_IN_HEX_DIGIT;
|
||||
|
||||
const StorageType truncated_bits =
|
||||
mantissa & ((StorageType(1) << shift_amount) - 1);
|
||||
const StorageType halfway_const = StorageType(1) << (shift_amount - 1);
|
||||
const AnyFloatStorageType truncated_bits =
|
||||
properties.mantissa & ((AnyFloatStorageType(1) << shift_amount) - 1);
|
||||
const AnyFloatStorageType halfway_const = AnyFloatStorageType(1)
|
||||
<< (shift_amount - 1);
|
||||
|
||||
mantissa >>= shift_amount;
|
||||
properties.mantissa >>= shift_amount;
|
||||
|
||||
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
|
||||
// Round to nearest, if it's exactly halfway then round to even.
|
||||
@@ -123,17 +145,17 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
|
||||
case FE_TONEAREST:
|
||||
// Round to nearest, if it's exactly halfway then round to even.
|
||||
if (truncated_bits > halfway_const)
|
||||
++mantissa;
|
||||
++properties.mantissa;
|
||||
else if (truncated_bits == halfway_const)
|
||||
mantissa = mantissa + (mantissa & 1);
|
||||
properties.mantissa = properties.mantissa + (properties.mantissa & 1);
|
||||
break;
|
||||
case FE_DOWNWARD:
|
||||
if (truncated_bits > 0 && is_negative)
|
||||
++mantissa;
|
||||
if (truncated_bits > 0 && properties.is_negative)
|
||||
++properties.mantissa;
|
||||
break;
|
||||
case FE_UPWARD:
|
||||
if (truncated_bits > 0 && !is_negative)
|
||||
++mantissa;
|
||||
if (truncated_bits > 0 && !properties.is_negative)
|
||||
++properties.mantissa;
|
||||
break;
|
||||
case FE_TOWARDZERO:
|
||||
break;
|
||||
@@ -142,9 +164,10 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
|
||||
|
||||
// If the rounding caused an overflow, shift the mantissa and adjust the
|
||||
// exponent to match.
|
||||
if (mantissa >= (StorageType(1) << (intended_digits * BITS_IN_HEX_DIGIT))) {
|
||||
mantissa >>= BITS_IN_HEX_DIGIT;
|
||||
exponent += BITS_IN_HEX_DIGIT;
|
||||
if (properties.mantissa >=
|
||||
(AnyFloatStorageType(1) << (intended_digits * BITS_IN_HEX_DIGIT))) {
|
||||
properties.mantissa >>= BITS_IN_HEX_DIGIT;
|
||||
properties.exponent += BITS_IN_HEX_DIGIT;
|
||||
}
|
||||
|
||||
mant_len = intended_digits;
|
||||
@@ -152,8 +175,8 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
|
||||
|
||||
size_t mant_cur = mant_len;
|
||||
size_t first_non_zero = 1;
|
||||
for (; mant_cur > 0; --mant_cur, mantissa >>= 4) {
|
||||
char mant_mod_16 = static_cast<char>(mantissa % 16);
|
||||
for (; mant_cur > 0; --mant_cur, properties.mantissa >>= 4) {
|
||||
char mant_mod_16 = static_cast<char>(properties.mantissa % 16);
|
||||
char new_digit = internal::int_to_b36_char(mant_mod_16);
|
||||
if (internal::isupper(to_conv.conv_name))
|
||||
new_digit = internal::toupper(new_digit);
|
||||
@@ -173,18 +196,19 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
|
||||
// 15 -> 5
|
||||
// 11 -> 4
|
||||
// 8 -> 3
|
||||
constexpr size_t EXP_LEN = (((LDBits::EXP_LEN * 5) + 15) / 16) + 1;
|
||||
constexpr size_t EXP_LEN = (((MAX_POSSIBLE_EXP_LEN * 5) + 15) / 16) + 1;
|
||||
char exp_buffer[EXP_LEN];
|
||||
|
||||
bool exp_is_negative = false;
|
||||
if (exponent < 0) {
|
||||
if (properties.exponent < 0) {
|
||||
exp_is_negative = true;
|
||||
exponent = -exponent;
|
||||
properties.exponent = -properties.exponent;
|
||||
}
|
||||
|
||||
size_t exp_cur = EXP_LEN;
|
||||
for (; exponent > 0; --exp_cur, exponent /= 10) {
|
||||
exp_buffer[exp_cur - 1] = internal::int_to_b36_char(exponent % 10);
|
||||
for (; properties.exponent > 0; --exp_cur, properties.exponent /= 10) {
|
||||
exp_buffer[exp_cur - 1] =
|
||||
internal::int_to_b36_char(properties.exponent % 10);
|
||||
}
|
||||
if (exp_cur == EXP_LEN) { // if nothing else was written, write a 0.
|
||||
exp_buffer[EXP_LEN - 1] = '0';
|
||||
|
||||
@@ -22,38 +22,46 @@
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
namespace printf_core {
|
||||
|
||||
struct InfNanFPBitsProperties {
|
||||
bool is_negative;
|
||||
bool mantissa_is_zero;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
InfNanFPBitsProperties
|
||||
get_inf_nan_fp_bits_properties(AnyFloatStorageType float_raw) {
|
||||
fputil::FPBits<T> float_bits(
|
||||
static_cast<typename fputil::FPBits<T>::StorageType>(float_raw));
|
||||
return {
|
||||
.is_negative = float_bits.is_neg(),
|
||||
.mantissa_is_zero = float_bits.get_mantissa() == 0,
|
||||
};
|
||||
}
|
||||
|
||||
template <WriteMode write_mode>
|
||||
LIBC_INLINE int convert_inf_nan(Writer<write_mode> *writer,
|
||||
const FormatSection &to_conv) {
|
||||
#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
using StorageType = UInt128;
|
||||
#else
|
||||
using StorageType = fputil::FPBits<long double>::StorageType;
|
||||
#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
|
||||
// All of the letters will be defined relative to variable a, which will be
|
||||
// the appropriate case based on the case of the conversion.
|
||||
bool is_negative;
|
||||
StorageType mantissa;
|
||||
InfNanFPBitsProperties properties;
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
if (to_conv.length_modifier == LengthModifier::Q) {
|
||||
properties = get_inf_nan_fp_bits_properties<float128>(to_conv.conv_val_raw);
|
||||
} else
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
if (to_conv.length_modifier == LengthModifier::L) {
|
||||
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
|
||||
fputil::FPBits<long double> float_bits(float_raw);
|
||||
is_negative = float_bits.is_neg();
|
||||
mantissa = float_bits.get_mantissa();
|
||||
if (to_conv.length_modifier == LengthModifier::L) {
|
||||
properties =
|
||||
get_inf_nan_fp_bits_properties<long double>(to_conv.conv_val_raw);
|
||||
} else
|
||||
#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
{
|
||||
fputil::FPBits<double>::StorageType float_raw =
|
||||
static_cast<fputil::FPBits<double>::StorageType>(to_conv.conv_val_raw);
|
||||
fputil::FPBits<double> float_bits(float_raw);
|
||||
is_negative = float_bits.is_neg();
|
||||
mantissa = float_bits.get_mantissa();
|
||||
properties = get_inf_nan_fp_bits_properties<double>(to_conv.conv_val_raw);
|
||||
}
|
||||
|
||||
char sign_char = 0;
|
||||
|
||||
if (is_negative)
|
||||
if (properties.is_negative)
|
||||
sign_char = '-';
|
||||
else if ((to_conv.flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
|
||||
sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
|
||||
@@ -73,7 +81,7 @@ LIBC_INLINE int convert_inf_nan(Writer<write_mode> *writer,
|
||||
|
||||
if (sign_char)
|
||||
RET_IF_RESULT_NEGATIVE(writer->write(sign_char));
|
||||
if (mantissa == 0) { // inf
|
||||
if (properties.mantissa_is_zero) { // inf
|
||||
RET_IF_RESULT_NEGATIVE(
|
||||
writer->write(internal::islower(to_conv.conv_name) ? "inf" : "INF"));
|
||||
} else { // nan
|
||||
|
||||
@@ -35,21 +35,25 @@
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
namespace printf_core {
|
||||
|
||||
template <typename T> struct int_type_of {
|
||||
template <typename T, typename U = void> struct int_type_of {
|
||||
using type = T;
|
||||
};
|
||||
template <> struct int_type_of<double> {
|
||||
using type = fputil::FPBits<double>::StorageType;
|
||||
};
|
||||
#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
template <> struct int_type_of<long double> {
|
||||
using type = fputil::FPBits<long double>::StorageType;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct int_type_of<T, cpp::enable_if_t<cpp::is_same_v<T, double>
|
||||
#if !defined(LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE)
|
||||
|| cpp::is_same_v<T, long double>
|
||||
#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
#if defined(LIBC_TYPES_HAS_FLOAT128)
|
||||
|| cpp::is_same_v<T, float128>
|
||||
#endif // LIBC_TYPES_HAS_FLOAT128
|
||||
>> {
|
||||
using type = typename fputil::FPBits<T>::StorageType;
|
||||
};
|
||||
|
||||
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
|
||||
template <typename T>
|
||||
struct int_type_of<cpp::enable_if<cpp::is_fixed_point_v<T>, T>> {
|
||||
struct int_type_of<T, cpp::enable_if<cpp::is_fixed_point_v<T>>> {
|
||||
using type = typename fixed_point::FXRep<T>::StorageType;
|
||||
};
|
||||
#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
|
||||
@@ -243,6 +247,11 @@ public:
|
||||
}
|
||||
break;
|
||||
#endif // LIBC_COPT_PRINTF_DISABLE_BITINT
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
case (LengthModifier::Q):
|
||||
section.has_conv = false;
|
||||
break;
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
}
|
||||
break;
|
||||
#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
|
||||
@@ -254,12 +263,20 @@ public:
|
||||
case ('A'):
|
||||
case ('g'):
|
||||
case ('G'):
|
||||
if (lm != LengthModifier::L) {
|
||||
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, double, conv_index);
|
||||
} else {
|
||||
switch (lm) {
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
case (LengthModifier::Q):
|
||||
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, float128, conv_index);
|
||||
break;
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
case (LengthModifier::L):
|
||||
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long double, conv_index);
|
||||
break;
|
||||
#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
default:
|
||||
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, double, conv_index);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
|
||||
@@ -396,6 +413,11 @@ private:
|
||||
case ('L'):
|
||||
++*local_pos;
|
||||
return {LengthModifier::L, 0};
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
case ('Q'):
|
||||
++*local_pos;
|
||||
return {LengthModifier::Q, 0};
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
case ('j'):
|
||||
++*local_pos;
|
||||
return {LengthModifier::j, 0};
|
||||
@@ -654,6 +676,11 @@ private:
|
||||
}
|
||||
break;
|
||||
#endif // LIBC_COPT_PRINTF_DISABLE_BITINT
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
case (LengthModifier::Q):
|
||||
conv_size = type_desc_from_type<void>();
|
||||
break;
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
}
|
||||
break;
|
||||
#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
|
||||
@@ -665,12 +692,21 @@ private:
|
||||
case ('A'):
|
||||
case ('g'):
|
||||
case ('G'):
|
||||
if (lm != LengthModifier::L)
|
||||
conv_size = type_desc_from_type<double>();
|
||||
switch (lm) {
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
case LengthModifier::Q:
|
||||
conv_size = type_desc_from_type<float128>();
|
||||
break;
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
else
|
||||
case LengthModifier::L:
|
||||
conv_size = type_desc_from_type<long double>();
|
||||
break;
|
||||
#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
|
||||
default:
|
||||
conv_size = type_desc_from_type<double>();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
|
||||
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
|
||||
|
||||
@@ -36,6 +36,11 @@
|
||||
#define LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
|
||||
#endif
|
||||
|
||||
#if defined(LIBC_TYPES_HAS_FLOAT128) && \
|
||||
!defined(LIBC_COPT_PRINTF_NO_CONVERT_FLOAT128)
|
||||
#define LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
#endif
|
||||
|
||||
// TODO(michaelrj): Provide a proper interface for these options.
|
||||
// LIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE
|
||||
// LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT
|
||||
|
||||
@@ -63,6 +63,11 @@ LIBC_INLINE int convert_write_int(Writer<write_mode> *writer,
|
||||
#endif // LIBC_COPT_PRINTF_DISABLE_BITINT
|
||||
*reinterpret_cast<uintmax_t *>(to_conv.conv_val_ptr) = written;
|
||||
break;
|
||||
#if defined(LIBC_TYPES_HAS_FLOAT128)
|
||||
case (LengthModifier::Q): // 'Q' is not valid for integer format; this case
|
||||
// should not be reachable.
|
||||
break;
|
||||
#endif // LIBC_TYPES_HAS_FLOAT128
|
||||
}
|
||||
return WRITE_OK;
|
||||
}
|
||||
|
||||
@@ -184,6 +184,7 @@ add_unittest_framework_library(
|
||||
libc.hdr.stdint_proxy
|
||||
libc.src.__support.FPUtil.fp_bits
|
||||
libc.src.stdio.printf_core.core_structs
|
||||
libc.src.stdio.printf_core.printf_config
|
||||
libc.test.UnitTest.string_utils
|
||||
)
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "src/__support/FPUtil/FPBits.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
#include "src/stdio/printf_core/core_structs.h"
|
||||
#include "src/stdio/printf_core/printf_config.h"
|
||||
|
||||
#include "test/UnitTest/StringUtils.h"
|
||||
#include "test/UnitTest/Test.h"
|
||||
@@ -71,6 +72,9 @@ static void display(FormatSection form) {
|
||||
CASE_LM(z);
|
||||
CASE_LM(t);
|
||||
CASE_LM(L);
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
CASE_LM(Q);
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
#ifndef LIBC_COPT_PRINTF_DISABLE_BITINT
|
||||
CASE_LM_BIT_WIDTH(w, form.bit_width);
|
||||
CASE_LM_BIT_WIDTH(wf, form.bit_width);
|
||||
@@ -84,10 +88,7 @@ static void display(FormatSection form) {
|
||||
reinterpret_cast<uintptr_t>(form.conv_val_ptr))
|
||||
<< "\n";
|
||||
else if (form.conv_name != '%')
|
||||
tlog << "\tvalue: "
|
||||
<< int_to_hex<fputil::FPBits<long double>::StorageType>(
|
||||
form.conv_val_raw)
|
||||
<< "\n";
|
||||
tlog << "\tvalue: " << int_to_hex(form.conv_val_raw) << "\n";
|
||||
}
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
@@ -173,6 +173,7 @@ add_fp_unittest(
|
||||
SRCS
|
||||
sprintf_test.cpp
|
||||
DEPENDS
|
||||
libc.src.stdio.printf_core.printf_config
|
||||
libc.src.stdio.sprintf
|
||||
libc.src.__support.FPUtil.fp_bits
|
||||
libc.include.inttypes
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "src/__support/macros/config.h"
|
||||
#include "src/stdio/printf_core/printf_config.h"
|
||||
#include "src/stdio/sprintf.h"
|
||||
|
||||
#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
|
||||
@@ -55,6 +56,44 @@ TEST(LlvmLibcSPrintfTest, Macros) {
|
||||
macro_test(PRIo64, 0123456712345671234567ll, "123456712345671234567");
|
||||
}
|
||||
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
|
||||
// Returns a non-negative float128 value:
|
||||
//
|
||||
// Result = (explicit_mantissa_bits_high << 64) | explicit_mantissa_bits_low) *
|
||||
// 2^(exponent - 112)
|
||||
//
|
||||
// This is a convenience so test values can be expressed with hex literals that
|
||||
// map directly to how they could appear as a combined native floating-point
|
||||
// literal. The high 15 bits of explicit_mantissa_bits_high should be zero.
|
||||
float128 make_f128(uint64_t explicit_mantissa_bits_high,
|
||||
uint64_t explicit_mantissa_bits_low, int exponent) {
|
||||
if (explicit_mantissa_bits_high == 0 && explicit_mantissa_bits_low == 0)
|
||||
return float128(0);
|
||||
|
||||
using BitsType = LIBC_NAMESPACE::fputil::FPBits<float128>;
|
||||
UInt128 mantissa = (UInt128(explicit_mantissa_bits_high) << 64) |
|
||||
UInt128(explicit_mantissa_bits_low);
|
||||
// exponent += LIBC_NAMESPACE::cpp::countl_zero(mantissa) -
|
||||
// (BitsType::STORAGE_LEN - BitsType::FRACTION_LEN);
|
||||
int biased_exponent = exponent - 1 + BitsType::EXP_BIAS;
|
||||
return BitsType::make_value(mantissa, biased_exponent).get_val();
|
||||
}
|
||||
|
||||
TEST(LlvmLibcSPrintfTest, MakeF128Helper) {
|
||||
EXPECT_TRUE(make_f128(0, 0, 0) == float128(0));
|
||||
EXPECT_TRUE(make_f128(0, 1, 0) == float128(0x1p-112));
|
||||
EXPECT_TRUE(make_f128(0x1'0000'0000'0000, 0, 0) == float128(1));
|
||||
EXPECT_TRUE(make_f128(0x1'FF00'0000'0000, 0, 3) ==
|
||||
float128((2.0 - 1.0 / 256)) * 8.0);
|
||||
#if defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
|
||||
EXPECT_TRUE(make_f128(0x1'2345'6789'0abc, 0x0123'4567'89ab'cdef, -1234) ==
|
||||
0x1.2345'6789'0abc'0123'4567'89ab'cdefp-1234L);
|
||||
#endif // LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128
|
||||
}
|
||||
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
|
||||
TEST(LlvmLibcSPrintfTest, SimpleNoConv) {
|
||||
char buff[64];
|
||||
int written;
|
||||
@@ -725,6 +764,10 @@ TEST(LlvmLibcSPrintfTest, FloatHexExpConv) {
|
||||
#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
|
||||
ASSERT_STREQ_LEN(written, buff, "0x1.999999999999999999999999999ap-4");
|
||||
#endif
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qa", float128(1) / float128(10));
|
||||
ASSERT_STREQ_LEN(written, buff, "0x1.999999999999999999999999999ap-4");
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%La", 1.0e1000L);
|
||||
#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
|
||||
@@ -734,6 +777,12 @@ TEST(LlvmLibcSPrintfTest, FloatHexExpConv) {
|
||||
#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
|
||||
ASSERT_STREQ_LEN(written, buff, "0x1.e71b63f3ba7b580af1a52d2a7379p+3321");
|
||||
#endif
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128) && \
|
||||
defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qa", 1.0e1000L);
|
||||
ASSERT_STREQ_LEN(written, buff, "0x1.e71b63f3ba7b580af1a52d2a7379p+3321");
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128 &&
|
||||
// LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%La", 1.0e-1000L);
|
||||
#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
|
||||
@@ -743,6 +792,12 @@ TEST(LlvmLibcSPrintfTest, FloatHexExpConv) {
|
||||
#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
|
||||
ASSERT_STREQ_LEN(written, buff, "0x1.0d152311513c28ce202627c06ec2p-3322");
|
||||
#endif
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128) && \
|
||||
defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qa", 1.0e-1000L);
|
||||
ASSERT_STREQ_LEN(written, buff, "0x1.0d152311513c28ce202627c06ec2p-3322");
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128 &&
|
||||
// LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128
|
||||
|
||||
// Min Width Tests.
|
||||
|
||||
@@ -849,6 +904,10 @@ TEST(LlvmLibcSPrintfTest, FloatHexExpConv) {
|
||||
#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
|
||||
ASSERT_STREQ_LEN(written, buff, "0x1.ap-4");
|
||||
#endif
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%.1Qa", float128(0.1));
|
||||
ASSERT_STREQ_LEN(written, buff, "0x1.ap-4");
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%.1La", 0xf.fffffffffffffffp16380L);
|
||||
#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
|
||||
@@ -858,6 +917,11 @@ TEST(LlvmLibcSPrintfTest, FloatHexExpConv) {
|
||||
#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
|
||||
ASSERT_STREQ_LEN(written, buff, "0x2.0p+16383");
|
||||
#endif
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%.1Qa",
|
||||
float128(0xf.fffffffffffffffp16380L));
|
||||
ASSERT_STREQ_LEN(written, buff, "0x2.0p+16383");
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
|
||||
// Rounding Mode Tests.
|
||||
|
||||
@@ -1034,6 +1098,11 @@ TEST(LlvmLibcSPrintfTest, FloatDecimalConv) {
|
||||
LIBC_NAMESPACE::fputil::FPBits<long double>::inf().get_val();
|
||||
long double ld_nan =
|
||||
LIBC_NAMESPACE::fputil::FPBits<long double>::quiet_nan().get_val();
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
float128 f128_inf = LIBC_NAMESPACE::fputil::FPBits<float128>::inf().get_val();
|
||||
float128 f128_nan =
|
||||
LIBC_NAMESPACE::fputil::FPBits<float128>::quiet_nan().get_val();
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%f", 1.0);
|
||||
ASSERT_STREQ_LEN(written, buff, "1.000000");
|
||||
@@ -1109,6 +1178,26 @@ TEST(LlvmLibcSPrintfTest, FloatDecimalConv) {
|
||||
ASSERT_STREQ_LEN(written, buff, "-NAN");
|
||||
#endif
|
||||
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qf", f128_inf);
|
||||
ASSERT_STREQ_LEN(written, buff, "inf");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qf", -f128_inf);
|
||||
ASSERT_STREQ_LEN(written, buff, "-inf");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%QF", f128_inf);
|
||||
ASSERT_STREQ_LEN(written, buff, "INF");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%QF", -f128_inf);
|
||||
ASSERT_STREQ_LEN(written, buff, "-INF");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qf", f128_nan);
|
||||
ASSERT_STREQ_LEN(written, buff, "nan");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%QF", f128_nan);
|
||||
ASSERT_STREQ_LEN(written, buff, "NAN");
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
|
||||
// Min Width Tests.
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%15f", 1.0);
|
||||
@@ -1986,6 +2075,145 @@ TEST(LlvmLibcSPrintfTest, FloatDecimalLongDoubleConv) {
|
||||
#endif // LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128
|
||||
}
|
||||
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
TEST(LlvmLibcSPrintfTest, FloatDecimalFloat128Conv) {
|
||||
char buff[1000];
|
||||
int written;
|
||||
|
||||
ForceRoundingMode r(RoundingMode::Nearest);
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qf", float128(1.0L));
|
||||
ASSERT_STREQ_LEN(written, buff, "1.000000");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%.Qf", float128(-2.5L));
|
||||
ASSERT_STREQ_LEN(written, buff, "-2");
|
||||
|
||||
// Some exceptionally difficult cases for 39-digit precision. (That's the
|
||||
// number of digits supported by the float320 algorithm, and should still be
|
||||
// correct under other algorithms. So these are still enabled even under
|
||||
// LIBC_COPT_FLOAT_TO_STR_REDUCED_PRECISION.)
|
||||
//
|
||||
// These were found by number-theoretic search to be the worst cases in terms
|
||||
// of being extremely close to the rounding boundary between two possible
|
||||
// decimal outputs. For example, the first of these cases has a true value
|
||||
// beginning with
|
||||
//
|
||||
// 2.245786964418815522831613614422112838795000000000000000000
|
||||
// 0000000000000000010767969...
|
||||
//
|
||||
// so you need to compute a _long_ way past the 39th digit to find out
|
||||
// whether to round the ...8795 up to 880 or not!
|
||||
//
|
||||
// The first half of these cases all rounded up; the second half all rounded
|
||||
// down. You can see that in both sections the final decimal digit is
|
||||
// sometimes odd and sometimes even, ruling out the possibility that we're
|
||||
// getting these right by mistakenly assuming them to be _exactly_ on the
|
||||
// boundary.
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'bde5'716b'ba8d, 0x7025'5b4b'e10e'0a0a, -3388));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"2.24578696441881552283161361442211283880e-1020");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'64b7'8def'c871, 0x2684'4909'80d8'0808, -3391));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"2.24578696441881552283161361442211283880e-1021");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'c0ee'd9d1'ea4b, 0x6f21'5acc'b15c'b42c, -4714));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"1.54362575487963943466308346767014523161e-1419");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'a393'eaaf'c3db, 0xabfc'd304'42ce'f525, -12600));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"1.72435694193924008931441874634575361189e-3793");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'b7a5'248b'af85, 0x133c'9b7b'f324'1f75, 11050));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"4.13346579244549095104252956440178208514e+3326");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'81bf'6d97'7f99, 0xff7b'd9de'bdd5'2815, 1359));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"1.89595297593127274811683259716608232064e+409");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'fcb9'cfd6'5f06, 0x8b75'8d30'ba19'a494, -4451));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"2.59258570015527007681686041122929728653e-1340");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'7d8b'5be0'c744, 0xe898'29e4'8b93'3b6f, -4448));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"1.55555142009316204609011624673757837192e-1339");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'dbb1'f01a'ef7b, 0x93c3'7ca0'0217'888c, -12205));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"1.57758077908296078543773740016012372216e-3674");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'fb03'7d72'fdf1, 0xa8aa'8bdf'c258'6fe3, 2580));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"8.99846610004600287527755301065037553046e+776");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'9af8'fe5f'ebf7, 0x51a7'9529'2e30'335d, 2052));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"8.30087814106622071390265113980150545241e+617");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'48c7'31e6'565f, 0x7486'10ed'be8c'f5e4, 2049));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"8.30087814106622071390265113980150545241e+616");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'8e78'bc76'e989, 0x98b7'bbf6'c8f8'0f2a, -5671));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"1.12473970318904704063044350553302771341e-1707");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'59a8'32c1'6a77, 0x97ae'c701'96dd'f9db, -16181));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"1.45896738321434823250358135932839533040e-4871");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'1486'8f01'21f9, 0x4625'6c01'457e'617c, -16184));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"1.45896738321434823250358135932839533040e-4872");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'0ca8'c4b5'25b1, 0x1285'06cd'c668'df43, 11017));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"2.94051951004764266903705588743096762647e+3316");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'fcd4'0bb4'9b18, 0xaa19'd66a'3c55'72de, 5547));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"1.29335350323078956384272678475060580129e+1670");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'0e5e'5151'0a65, 0x3e74'4d6b'84ef'ed86, -13613));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"1.26585813611514592337442314391432904094e-4098");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'ff41'f8aa'97e6, 0x76e9'5b1a'6a77'51fa, 16366));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"9.06377119787295161934827646572467920486e+4926");
|
||||
written = LIBC_NAMESPACE::sprintf(
|
||||
buff, "%#.39Qg",
|
||||
make_f128(0x1'4770'bf66'ccaf, 0xd7d8'0ac9'bb3d'ded7, -1843));
|
||||
ASSERT_STREQ_LEN(written, buff,
|
||||
"2.03521509642091239545213340619368190283e-555");
|
||||
}
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
|
||||
TEST(LlvmLibcSPrintfTest, FloatExponentConv) {
|
||||
char buff[1000];
|
||||
int written;
|
||||
@@ -2507,6 +2735,56 @@ TEST(LlvmLibcSPrintfTest, FloatExponentLongDoubleConv) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
TEST(LlvmLibcSPrintfTest, FloatExponentFloat128Conv) {
|
||||
char buff[1000];
|
||||
int written;
|
||||
|
||||
ForceRoundingMode r(RoundingMode::Nearest);
|
||||
|
||||
written =
|
||||
LIBC_NAMESPACE::sprintf(buff, "%.9Qe", float128(1000000000500000000.1L));
|
||||
ASSERT_STREQ_LEN(written, buff, "1.000000001e+18");
|
||||
|
||||
written =
|
||||
LIBC_NAMESPACE::sprintf(buff, "%.9Qe", float128(1000000000500000000.0L));
|
||||
ASSERT_STREQ_LEN(written, buff, "1.000000000e+18");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qe", float128(1e100L));
|
||||
ASSERT_STREQ_LEN(written, buff, "1.000000e+100");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qe", float128(1.0L));
|
||||
ASSERT_STREQ_LEN(written, buff, "1.000000e+00");
|
||||
|
||||
#if !defined(LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE)
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qe",
|
||||
float128(0xf.fffffffffffffffp+16380L));
|
||||
ASSERT_STREQ_LEN(written, buff, "1.189731e+4932");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qe", float128(1e1000L));
|
||||
ASSERT_STREQ_LEN(written, buff, "1.000000e+1000");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qe", float128(1e4900L));
|
||||
ASSERT_STREQ_LEN(written, buff, "1.000000e+4900");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qe", float128(1.2345678e4900L));
|
||||
ASSERT_STREQ_LEN(written, buff, "1.234568e+4900");
|
||||
|
||||
#ifndef LIBC_COPT_FLOAT_TO_STR_REDUCED_PRECISION
|
||||
// Minimum normal + epsilon
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%.20Qe",
|
||||
float128(3.36210314311209350663E-4932L));
|
||||
ASSERT_STREQ_LEN(written, buff, "3.36210314311209350663e-4932");
|
||||
|
||||
// Minimum subnormal
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%.20Qe",
|
||||
float128(3.64519953188247460253E-4951L));
|
||||
ASSERT_STREQ_LEN(written, buff, "3.64519953188247460253e-4951");
|
||||
#endif // LIBC_COPT_FLOAT_TO_STR_REDUCED_PRECISION
|
||||
#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE
|
||||
}
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
|
||||
TEST(LlvmLibcSPrintfTest, FloatAutoConv) {
|
||||
char buff[1000];
|
||||
int written;
|
||||
@@ -3040,6 +3318,33 @@ TEST(LlvmLibcSPrintfTest, FloatAutoLongDoubleConv) {
|
||||
ASSERT_STREQ_LEN(written, big_buff, "1e+4900");
|
||||
}
|
||||
|
||||
#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
|
||||
TEST(LlvmLibcSPrintfTest, FloatAutoFloat128Conv) {
|
||||
char buff[1000];
|
||||
int written;
|
||||
|
||||
ForceRoundingMode r(RoundingMode::Nearest);
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qg", float128(9.99999999999e-100L));
|
||||
ASSERT_STREQ_LEN(written, buff, "1e-99");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qg", float128(1e100L));
|
||||
ASSERT_STREQ_LEN(written, buff, "1e+100");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qg", float128(1.0L));
|
||||
ASSERT_STREQ_LEN(written, buff, "1");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qg", float128(0.1L));
|
||||
ASSERT_STREQ_LEN(written, buff, "0.1");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qg", float128(1e1000L));
|
||||
ASSERT_STREQ_LEN(written, big_buff, "1e+1000");
|
||||
|
||||
written = LIBC_NAMESPACE::sprintf(buff, "%Qg", float128(1e4900L));
|
||||
ASSERT_STREQ_LEN(written, big_buff, "1e+4900");
|
||||
}
|
||||
#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
|
||||
|
||||
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
|
||||
|
||||
#if defined(LIBC_COMPILER_HAS_FIXED_POINT) && \
|
||||
|
||||
Reference in new issue
Block a user