diff --git a/examples/linux/printf/SConstruct b/examples/linux/printf/SConstruct new file mode 100644 index 000000000..4cf8e3f74 --- /dev/null +++ b/examples/linux/printf/SConstruct @@ -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') diff --git a/examples/linux/printf/main.cpp b/examples/linux/printf/main.cpp new file mode 100644 index 000000000..680629c8c --- /dev/null +++ b/examples/linux/printf/main.cpp @@ -0,0 +1,59 @@ +#include +#include + +#include + +// 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; +} diff --git a/examples/linux/printf/project.cfg b/examples/linux/printf/project.cfg new file mode 100644 index 000000000..2bd351d16 --- /dev/null +++ b/examples/linux/printf/project.cfg @@ -0,0 +1,6 @@ +[general] +name = printf_hosted + +[build] +device = hosted +buildpath = ${xpccpath}/build/${name} diff --git a/src/xpcc/io/iostream.hpp b/src/xpcc/io/iostream.hpp index bf0aad5bf..93f1e44b3 100644 --- a/src/xpcc/io/iostream.hpp +++ b/src/xpcc/io/iostream.hpp @@ -33,7 +33,7 @@ namespace xpcc */ class IOStream { -public : +public: /** * @param device device to write the stream to * @@ -444,7 +444,7 @@ public : IOStream& vprintf(const char *fmt, va_list vlist); -protected : +protected: void writeInteger(int16_t value); @@ -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 { diff --git a/src/xpcc/io/iostream_printf.cpp b/src/xpcc/io/iostream_printf.cpp index 9d2ffe568..26e83563c 100644 --- a/src/xpcc/io/iostream_printf.cpp +++ b/src/xpcc/io/iostream_printf.cpp @@ -10,6 +10,7 @@ #include #include // snprintf() #include +#include // xpcc::pow #include "iostream.hpp" @@ -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 != '%') { @@ -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') { @@ -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; @@ -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 */ @@ -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; @@ -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); + } +}