Skip to content

Commit

Permalink
Added better support for float
Browse files Browse the repository at this point in the history
  • Loading branch information
MaJerle committed Aug 30, 2020
1 parent a145bda commit 7cc8242
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 28 deletions.
7 changes: 6 additions & 1 deletion dev/VisualStudio/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ main(void) {

lwprintf_init(lwprintf_output);

/* Float tests */
for (float a = 0.0f; a < 1.0f; a += 0.01f) {
printf_run(NULL, "%10f; %10.1f; %10.0f", 1.99f + a, 1.99f + a, 1.99f + a);
}
return 0;

additional_format_specifiers();

printf_run(" 28", "% 3u", (unsigned)28);
Expand Down Expand Up @@ -130,7 +136,6 @@ main(void) {
printf_run(NULL, "0X%p", &tests_passed);
printf_run(NULL, "0x%p", &tests_passed);


/* Print final output */
printf("\r\n\r\n------------------------\r\n\r\n");
printf("Number of tests passed: %d\r\n", (int)tests_passed);
Expand Down
19 changes: 16 additions & 3 deletions docs/user-manual/thread-safety.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,23 @@
Thread safety
=============

With default configuration, LwPRINTF library is *not* thread safe.
This means whenever it is used with operating system, user must resolve it with care.
LwPRINTF uses re-entrant functions, especially the one that format string to user application buffer.
It is fully allowed to access to the same LwPRINTF instance from multiple operating-system threads.

Library has locking mechanism support for thread safety, which needs to be enabled manually.
However, when it comes to direct print functions, such as :cpp:func:`lwprintf_printf_ex` (or any other similar),
calling those functions from multiple threads may introduce mixed output stream of data.

This is due to the fact that direct printing functions use same output function
to print single character. When called from multiple threads, one thread
may preempt another, causing strange output string.

LwPRINTF therefore comes with a solution that introduces mutexes to lock print functions
when in use from within single thread context.

.. tip::
If application does not have any issues concerning mixed output,
it is safe to disable OS support in OS environment.
This will not have any negative effect on performance or memory corruption.

.. tip::
To enable thread-safety support, parameter ``LWPRINTF_CFG_OS`` must be set to ``1``.
Expand Down
7 changes: 7 additions & 0 deletions lwprintf/src/include/lwprintf/lwprintf.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ extern "C" {
*/
#define LWPRINTF_UNUSED(x) ((void)(x))

/**
* \brief Calculate size of statically allocated array
* \param[in] x: Input array
* \return Number of array elements
*/
#define LWPRINTF_ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))

/**
* \brief Forward declaration for LwPRINTF instance
*/
Expand Down
72 changes: 48 additions & 24 deletions lwprintf/src/lwprintf/lwprintf.c
Original file line number Diff line number Diff line change
Expand Up @@ -470,14 +470,22 @@ prv_signed_longlong_int_to_str(lwprintf_int_t* p, signed long long int num) {
*/
static int
prv_double_to_str(lwprintf_int_t* p, double num) {
#if LWPRINTF_CFG_SUPPORT_LONG_LONG
long long integer_part, decimal_part, tmp;
char str[22];
#else
long integer_part, decimal_part, tmp;
char str[11];
#endif
double decimal_part_dbl, diff;
size_t i;
int digits_cnt;
char str[11];

#if LWPRINTF_CFG_SUPPORT_LONG_LONG
/* Powers of 10 from beginning up to precision level */
static const powers_of_10[] = { 1E0, 1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, 1E9 };
static const long long int powers_of_10[] = { 1E00, 1E01, 1E02, 1E03, 1E04, 1E05, 1E06, 1E07, 1E08, 1E09,
1E10, 1E11, 1E12, 1E13, 1E14, 1E15, 1E16, 1E17, 1E18};
#endif

/* Check for corner cases */
if (num != num) {
Expand All @@ -503,8 +511,8 @@ prv_double_to_str(lwprintf_int_t* p, double num) {
}

/* Check precision data */
if (p->m.precision > 9) {
p->m.precision = 9; /* Limit to maximum precision */
if (p->m.precision > LWPRINTF_ARRAYSIZE(powers_of_10)) {
p->m.precision = LWPRINTF_ARRAYSIZE(powers_of_10); /* Limit to maximum precision */
} else if (!p->m.flags.precision) {
p->m.flags.precision = 1;
p->m.precision = LWPRINTF_CFG_FLOAT_PRECISION_DEFAULT; /* Default prevision when not used */
Expand All @@ -519,46 +527,62 @@ prv_double_to_str(lwprintf_int_t* p, double num) {
/* Rounding check */
if (diff > 0.5f) {
++decimal_part;
if (decimal_part > powers_of_10[p->m.precision]) {
if (decimal_part >= powers_of_10[p->m.precision]) {
decimal_part = 0;
++integer_part;
}
}

/* @todo: When no precision is used, check if need to round up anything */
if (p->m.precision == 0) {

} else if (diff < 0.5f) {
/* Used in separate if, since comparing float to == will certainly result to false */
} else {
/* Difference is exactly 0.5 */
if (decimal_part == 0) {
++integer_part;
} else {
++decimal_part;
}
}

/* Calculate number of digits for integer part */
for (digits_cnt = 0, tmp = integer_part; tmp > 0; ++digits_cnt, tmp /= 10) {}
if (integer_part == 0) {
digits_cnt = 1;
} else {
for (digits_cnt = 0, tmp = integer_part; tmp > 0; ++digits_cnt, tmp /= 10) {}
}
if (p->m.precision > 0) {
/* Add precision digits + dot separator */
digits_cnt += p->m.precision + 1;
}

/* Output strings */
prv_out_str_before(p, digits_cnt);
for (i = 0; integer_part > 0; integer_part /= 10, ++i) {
str[i] = (integer_part % 10) + '0';
if (integer_part == 0) {
str[0] = '0';
i = 1;
} else {
for (i = 0; integer_part > 0; integer_part /= 10, ++i) {
str[i] = (integer_part % 10) + '0';
}
}
/* Output integer part */
for (; i > 0; --i) {
p->out_fn(p, str[i - 1]);
}
p->out_fn(p, '.');
for (i = 0; decimal_part > 0; decimal_part /= 10, ++i) {
str[i] = (decimal_part % 10) + '0';
}
for (; i > 0; --i) {
p->out_fn(p, str[i - 1]);
/* Output decimal part */
if (p->m.precision > 0) {
p->out_fn(p, '.');
for (i = 0; decimal_part > 0; decimal_part /= 10, ++i) {
str[i] = (decimal_part % 10) + '0';
}
for (size_t x = i; x < p->m.precision; ++x) {
p->out_fn(p, '0');
}
for (; i > 0; --i) {
p->out_fn(p, str[i - 1]);
}
}
prv_out_str_after(p, digits_cnt);

/* Output integer part */
/* Output decimal part */

/* Process number itself */
return 0;
return 1;
}

#endif /* LWPRINTF_CFG_SUPPORT_TYPE_FLOAT */
Expand Down

0 comments on commit 7cc8242

Please sign in to comment.