LibC: implement printf conversions e, E, f, F

This commit is contained in:
Bananymous 2023-05-10 22:36:03 +03:00
parent ac12132ac0
commit 48edc38817
1 changed files with 80 additions and 31 deletions

View File

@ -1,6 +1,8 @@
#include <BAN/Traits.h> #include <BAN/Traits.h>
#include <BAN/Math.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <math.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -35,7 +37,7 @@ static void integer_to_string(char* buffer, T value, int base, bool upper, const
} }
else if (options.width != -1) else if (options.width != -1)
width = options.width; width = options.width;
auto digit_char = [](int digit, bool upper) auto digit_char = [](int digit, bool upper)
{ {
if (digit < 10) if (digit < 10)
@ -95,16 +97,6 @@ static void integer_to_string(char* buffer, T value, int base, bool upper, const
buffer[offset++] = '\0'; buffer[offset++] = '\0';
} }
template<BAN::floating_point T>
static void floating_point_to_exponent_string(char* buffer, T value, bool upper, const format_options_t options)
{
int percision = 6;
if (options.percision != -1)
percision = options.percision;
strcpy(buffer, "??e??");
}
template<BAN::floating_point T> template<BAN::floating_point T>
static void floating_point_to_string(char* buffer, T value, bool upper, const format_options_t options) static void floating_point_to_string(char* buffer, T value, bool upper, const format_options_t options)
{ {
@ -114,7 +106,8 @@ static void floating_point_to_string(char* buffer, T value, bool upper, const fo
int offset = 0; int offset = 0;
if (value < 0) // Add sign if needed
if (value < (T)0.0)
{ {
buffer[offset++] = '-'; buffer[offset++] = '-';
value = -value; value = -value;
@ -124,42 +117,98 @@ static void floating_point_to_string(char* buffer, T value, bool upper, const fo
else if (options.show_plus_sign_as_space) else if (options.show_plus_sign_as_space)
buffer[offset++] = ' '; buffer[offset++] = ' ';
int exponent = 1; // Round last digit
while (value > exponent * 10) value += (T)0.5 * BAN::Math::pow<T>(10.0, -percision);
exponent *= 10;
T fractional = value; // Add integer part of the decimal
while (exponent >= 1) if (value < (T)1.0)
{ {
int scale = 0; buffer[offset++] = '0';
for (; scale < 10; scale++) value *= (T)10.0;
if (fractional < T(exponent * (scale + 1))) }
break; else
buffer[offset++] = '0' + scale; {
fractional -= T(exponent * scale); int exponent = (int)BAN::Math::log10<T>(value);
exponent /= 10; T magnitude = BAN::Math::pow<T>(10, exponent);
value /= magnitude;
for (int i = 0; i <= exponent; i++)
{
int digit = 0;
for (; digit < 10; digit++)
if (value < digit + 1)
break;
buffer[offset++] = '0' + digit;
value -= (T)digit;
value *= (T)10.0;
}
} }
if (!options.alternate_form && percision <= 0) // We are done if the decimal part is not written
if (percision == 0 && !options.alternate_form)
{ {
buffer[offset++] = '\0'; buffer[offset++] = '\0';
return; return;
} }
buffer[offset++] = '.'; buffer[offset++] = '.';
// Add the 'percision' digits after decimal point
for (int i = 0; i < percision; i++) for (int i = 0; i < percision; i++)
{ {
fractional *= T(10.0); int digit = 0;
if (i == percision - 1) for (; digit < 10; digit++)
fractional += T(0.5); if (value < digit + 1)
int digit = fractional; break;
buffer[offset++] = '0' + digit; buffer[offset++] = '0' + digit;
fractional -= T(digit); value -= (T)digit;
value *= (T)10.0;
} }
buffer[offset++] = '\0'; buffer[offset++] = '\0';
} }
template<BAN::floating_point T>
static void floating_point_to_exponent_string(char* buffer, T value, bool upper, const format_options_t options)
{
int percision = 6;
if (options.percision != -1)
percision = options.percision;
int offset = 0;
// Add sign if needed
if (value < (T)0.0)
{
buffer[offset++] = '-';
value = -value;
}
else if (options.show_plus_sign)
buffer[offset++] = '+';
else if (options.show_plus_sign_as_space)
buffer[offset++] = ' ';
// Calculate which number to put as exponent
int exponent = 0;
if (value != (T)0.0)
{
exponent = (int)floorl(BAN::Math::log10<T>(value));
value /= BAN::Math::pow<T>(10.0, exponent);
}
// Add first numbers before 'e'
floating_point_to_string<T>(buffer + offset, value, upper, options);
offset = strlen(buffer);
// Add the exponent part
buffer[offset++] = (upper ? 'E' : 'e');
format_options_t exponent_options;
exponent_options.show_plus_sign = true;
exponent_options.zero_padded = true;
exponent_options.width = 3;
integer_to_string<int>(buffer + offset, exponent, 10, upper, exponent_options);
}
extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun)(int, void*), void* data) extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun)(int, void*), void* data)
{ {
int written = 0; int written = 0;