Skip to content
This repository has been archived by the owner on Jan 7, 2019. It is now read-only.

[io] IOStream: printf now supports basic %m.nf float formatting. #99

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
33 changes: 33 additions & 0 deletions examples/linux/printf/SConstruct
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# path to the xpcc root directory
rootpath = '../../..'

env = Environment(tools = ['xpcc'], toolpath = [rootpath + '/scons/site_tools'])

# find all source files
files = env.FindFiles('.')

# build the program
program = env.Program(target = env['XPCC_CONFIG']['general']['name'], source = files.sources)

# build the xpcc library
env.XpccLibrary()

# build xpcc_git_info.hpp and xpcc_build_info.hpp file
env.GitInfoHeader()
env.BuildInfoHeader()

# build xpcc_build_info.hpp file
env.BuildInfoHeader()

# create a file called 'defines.hpp' with all preprocessor defines if necessary
env.Defines()

env.Alias('size', env.Size(program))
env.Alias('symbols', env.Symbols(program))
env.Alias('defines', env.ShowDefines())

env.Alias('build', program)
env.Alias('run', env.Run(program))
env.Alias('all', ['build', 'run'])

env.Default('all')
59 changes: 59 additions & 0 deletions examples/linux/printf/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include <xpcc/architecture.hpp>
#include <xpcc/debug/logger.hpp>

#include <xpcc_build_info.hpp>

// Set the log level
#undef XPCC_LOG_LEVEL
#define XPCC_LOG_LEVEL xpcc::log::INFO

int
main()
{
// Let's print some information that is provided in the xpcc_build_info.hpp
XPCC_LOG_INFO << "Machine: " << XPCC_BUILD_MACHINE << xpcc::endl;
XPCC_LOG_INFO << "User: " << XPCC_BUILD_USER << xpcc::endl;
XPCC_LOG_INFO << "Os: " << XPCC_BUILD_OS << xpcc::endl;
XPCC_LOG_INFO << "Compiler: " << XPCC_BUILD_COMPILER << xpcc::endl;

XPCC_LOG_INFO << "Compare xpcc's printf and xpcc with glibc's printf" << xpcc::endl;

int32_t ii = -42;

char format_string[] = ">>%5d<<";

XPCC_LOG_INFO << "xpcc ";
XPCC_LOG_INFO.printf(format_string, ii);
XPCC_LOG_INFO << xpcc::endl;

char buf[32] = {' '};
sprintf(buf, format_string, ii);
XPCC_LOG_INFO.printf("glibc %s", buf);
XPCC_LOG_INFO << xpcc::endl;

float ff_testvector[] = {123.556789, -123.4};

for (size_t ii = 0; ii < XPCC__ARRAY_SIZE(ff_testvector); ++ii) {
float ff = ff_testvector[ii];

for (uint_fast8_t width = 1; width < 10; ++width)
{
for (uint_fast8_t width_frac = 1; width_frac < 10; ++width_frac)
{
char fmt_str[10];
sprintf(fmt_str, ">>%%%d.%df<<", width, width_frac);
XPCC_LOG_INFO << "xpcc ";
XPCC_LOG_INFO.printf(fmt_str, ff);
XPCC_LOG_INFO << xpcc::endl;

char buf[23];
sprintf(buf, fmt_str, ff);
XPCC_LOG_INFO.printf("glibc %s", buf);

XPCC_LOG_INFO << xpcc::endl << xpcc::endl;
}
}
}

return 0;
}
6 changes: 6 additions & 0 deletions examples/linux/printf/project.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[general]
name = printf_hosted

[build]
device = hosted
buildpath = ${xpccpath}/build/${name}
9 changes: 6 additions & 3 deletions src/xpcc/io/iostream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace xpcc
*/
class IOStream
{
public :
public:
/**
* @param device device to write the stream to
*
Expand Down Expand Up @@ -444,7 +444,7 @@ public :
IOStream&
vprintf(const char *fmt, va_list vlist);

protected :
protected:
void
writeInteger(int16_t value);

Expand Down Expand Up @@ -486,7 +486,10 @@ protected :
writeDouble(const double& value);
#endif

private :
void
writeUnsignedInteger(unsigned long unsignedValue, uint_fast8_t base, size_t width, char fill, bool isNegative);

private:
enum class
Mode
{
Expand Down
135 changes: 101 additions & 34 deletions src/xpcc/io/iostream_printf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <stdarg.h>
#include <stdio.h> // snprintf()
#include <stdlib.h>
#include <xpcc/math/utils/misc.hpp> // xpcc::pow

#include "iostream.hpp"

Expand All @@ -33,9 +34,11 @@ xpcc::IOStream::vprintf(const char *fmt, va_list ap)
// for all chars in format (fmt)
while ((c = *fmt++) != 0)
{
bool isSigned = 0;
bool isLong = 0;
bool isLongLong = 0;
bool isSigned = false;
bool isLong = false;
bool isLongLong = false;
bool isFloat = false;
bool isNegative = false;

if (c != '%')
{
Expand All @@ -45,6 +48,7 @@ xpcc::IOStream::vprintf(const char *fmt, va_list ap)
c = *fmt++;

size_t width = 0;
size_t width_frac = 0;
char fill = ' ';
if (c == '0')
{
Expand All @@ -57,6 +61,15 @@ xpcc::IOStream::vprintf(const char *fmt, va_list ap)
c = *fmt++;
}

if (c == '.') {
c = *fmt++;

if (c >= '0' && c <= '9') {
width_frac = c - '0';
}
c = *fmt++;
}

if (c == 'l')
{
isLong = true;
Expand Down Expand Up @@ -88,6 +101,10 @@ xpcc::IOStream::vprintf(const char *fmt, va_list ap)
}
continue;

case 'f':
isFloat = true;
break;

case 'd':
isSigned = true;
/* no break */
Expand All @@ -107,6 +124,44 @@ xpcc::IOStream::vprintf(const char *fmt, va_list ap)


// Number output
if (isFloat)
{
// va_arg(ap, float) not allowed
float float_value = va_arg(ap, double);

if (float_value < 0)
{
float_value = -float_value; // make it positive
isNegative = true;
}

// 1) Print integer part
int width_integer = width - width_frac - 1;
if (width_integer < 0) {
width_integer = 0;
}

writeUnsignedInteger((unsigned int)float_value, base, width_integer, fill, isNegative);

// 2) Decimal dot
this->device->write('.');

// 3) Fractional part
float_value = float_value - ((int) float_value);
float_value *= xpcc::pow(10, width_frac);

// Alternative: Smaller code size but probably less precise
// for (uint_fast8_t ii = 0; ii < width_frac; ++ii) {
// float_value = float_value * (10.0);
// }

// Add 1/2 for roundig of last digit
float_value += 0.5;

// Print fractional part
writeUnsignedInteger((unsigned int)float_value, base, width_frac, '0', false);
}
else
{
unsigned long unsignedValue;

Expand All @@ -127,50 +182,62 @@ xpcc::IOStream::vprintf(const char *fmt, va_list ap)
{
if (signedValue < 0)
{
isNegative = true;
signedValue = -signedValue; // make it positive
this->device->write('-');
if (width) {
--width;
}
}
}
unsignedValue = (unsigned long) signedValue;
}

{
char scratch[26];
writeUnsignedInteger(unsignedValue, base, width, fill, isNegative);
}
}

ptr = scratch + sizeof(scratch);
*--ptr = 0;
do
{
char ch = (unsignedValue % base) + '0';
return *this;
}

if (ch > '9') {
ch += 'A' - '9' - 1;
}
void
xpcc::IOStream::writeUnsignedInteger(
unsigned long unsignedValue, uint_fast8_t base,
size_t width, char fill, bool isNegative)
{
char scratch[26];

*--ptr = ch;
unsignedValue /= base;
char *ptr = scratch + sizeof(scratch);
*--ptr = 0;
do
{
char ch = (unsignedValue % base) + '0';

if (width) {
--width;
}
} while (unsignedValue);
if (ch > '9') {
ch += 'A' - '9' - 1;
}

// insert padding chars
while (width--) {
*--ptr = fill;
}
*--ptr = ch;
unsignedValue /= base;

// output result
while ((c = *ptr++)) {
this->device->write(c);
}
}
if (width) {
--width;
}
} while (unsignedValue);

// Insert minus sign if needed
if (isNegative)
{
*--ptr = '-';
if (width) {
--width;
}
}

return *this;
}
// insert padding chars
while (width--) {
*--ptr = fill;
}

// output result
char ch;
while ((ch = *ptr++)) {
this->device->write(ch);
}
}