Giter Club home page Giter Club logo

printf's Issues

left aligned fields don't obey field field widths

Attaching a test to reproduce and a proposed patch to fix.

Try running the test with linux system printf and the result is:

Print with precison -2:
|    9| |9 | |    9|
|   10| |10| |   10|
Print with precision -12:
|    9| |9           | |    9|
|   10| |10          | |   10|

With your printf:

Print with precison -2:
|    9| |9| |    9|
|   10| |10| |   10|
Print with precision -12:
|    9| |9  | |    9|
|   10| |10 | |   10|

Note the missing padding.

See the attached proposed fix.

printf.patch.txt
test_printf.c.txt

Scawy big unkown type UwU

Hi!!

Using your kawaii pwintf impwementation,
the compiler gets all funny about some silly size_t.

printf.h:78:29: error: unknown type name 'size_t'
 int snprintf(char* buffer, size_t count, const char* format, ...);
                                       ^~~~~~

It even dwew a line for us owo
I did some wesearch and it said, there's no sizy-wizy_t in stdarg.h.
Instead, I moved stddef.h fwom pwintf.c to pwintf.h, because
stddef.h is like totally the hood of size_t.

This fixie wixies the pwoblem
xD *starts twerking*

Digit missing from negative numbers in certain cases

Hi, it seems that when the width specifier matches the number of digits in a negative number, one of the digits gets removed from the output, i.e.

printf_ ("%2d", -123);   // --> prints -123 (ok)
printf_ ("%3d", -123);   // --> printf -23 ('1' is missing)
printf_ ("%4d", -123);   // --> printf -123 (ok)

The same issue happens with ("%1d", -1) or ("%2d", -12), for example.

The point where this happens is this line:

// when len == width, removes the last digit in the inverted output, 
// before writing the `-` character
if (len && (len == width) && (negative || (flags & FLAGS_PLUS) || (flags & FLAGS_SPACE))) {
    len--;
}

Can you made it more configurable?

Hi,

On small targets I do not use Float, Long and I also do not need a big buffer size. Can you made the code more configurable. I want to use the source as submodule as it is without any changes if possible. So it would be gread if you can add this or something else which solves the problem

// buffer size used for printf (created on stack)
#ifndef PRINTF_BUFFER_SIZE
#define PRINTF_BUFFER_SIZE    128U
#endif

// ntoa conversion buffer size, this must be big enough to hold one converted numeric number (created on stack)
#ifndef NTOA_BUFFER_SIZE
#define NTOA_BUFFER_SIZE      32U
#endif

// ftoa conversion buffer size, this must be big enough to hold one converted float number (created on stack)
#ifndef FTOA_BUFFER_SIZE
#define FTOA_BUFFER_SIZE      32U
#endif

// define this to support floating point (%f)
#ifndef PRINTF_NO_FLOAT_SUPPORT
#define PRINTF_FLOAT_SUPPORT
#endif

// define this to support long long types (%llu or %p)
#ifndef PRINTF_NO_LONG_LONG_SUPPORT
#define PRINTF_LONG_LONG_SUPPORT
#endif

so no I can use

#include "printf_config.h" 
#include "printf.h"

In the printf config i use

#define PRINTF_BUFFER_SIZE 32
#define PRINTF_NO_FLOAT_SUPPORT
#define PRINTF_NO_LONG_LONG_SUPPORT

many thanks
cheers
mathias

Some ideas / notes

There are some possible optimizations (at least for ARM CPU):

Only 4 arguments are passed in registers, remaining arguments has to be pushed on stack and stack frame may be created


out,buffer, maxlen are passed around a lot. This can be replaced by passing

struct out_object {
   void  (*fn)(out_object * out, char character, size_t idx);
}

struct out_object_buffer {
  struct out_object_base base;
  char* buffer;
}

static void out_buffer(out_object * out, char character, size_t idx) 
   struct out_object_buffer *self = (struct out_object_buffer*)out;
   self->buffer[idx] = character;
}

struct out_object_buffer_n {
   struct out_object_base base;
   char* buffer;
   size_t maxlen;
}

static void out_buffer_n(out_object * out, char character, size_t idx) 
   struct out_object_buffer_n *self = (struct out_object_buffer_n*)out;
   if(idx < self->maxlen)
      self->buffer[idx] = character;
}

In code, call out->fn(out, c, idx);. fn is only single memory fetch and first argument is single register move)

idx can be hidden in buffer object too (increment buffer in object, keep maxlen as pointer to end of buffer). This will avoid all idx handling in printf code, which must make no difference (idx must be incremented after each out call or _out_fct won't work)

BTW: inline makes no sense for _out_* functions, address for function is taken, so no inlining is possible unless _vsnprintf is inlined too


flags, base, prec, width, possibly negative (in flags?)may be packed into structure and passed by pointer. This will only need some care when this structure is modified.


There is subtle bug in _out_char - printf("X%cX", '\0'); will emit only 2 characters instead of three. Special-casing termination (possibly even as extra function pointer in out_object will handle this, it will improve code efficiency a bit (1 skipped test for each character) and as a bonus it will make it possible to implement buffering output function (flush on chunk size or end of format)


Another nice optimization would be to replace character output with write()-like function. This will considerably limit number of through-pointer calls and save some instructions (test buffer size only once per out call, then memcpy [possibly optimizing copy to use word access])
All that is necessary is to emit *format only when % is encountered and implement in-place reversing in out_rev and possibly use const char pad0[8]="00000000"; const char padSpc[8]=" "; for optimized padding.



I can (and probably will) make most of the changes, are you interested in merging them?

Not zero-terminating on overflow

Couple of minor issues I've spotted:

  • snprintf is not zero terminating the buffer if it runs out of bytes, which is potentially very dangerous. Only size - 1 characters may be written
    • there are, in fact, multiple cases of those:
      • inside _vsnprintf, can easily be fixed
      • inside the different ntoa's / ftoa's, potentially a little more annoying to fix
  • snprintf's return value is indicating how much has been written, not how much would've been written (if the buffer length would've been ignored)
  • snprintf cannot be used to determine the required buffer length (that is, calling it with a NULL buffer and size 0)

Here are a couple test-cases to reproduce:

int size = test::snprintf(buf, 10, "hello, world");
REQUIRE(size == 12); // fails, 9 is returned instead

int size = test::snprintf(buf, 3, "%d", 10000);
REQUIRE(size == 2); // fails, 5 is returned and the buffer is overflowed
REQUIRE(strlen(buf) == 2);
REQUIRE(buf[0] == '1');
REQUIRE(buf[1] == '2');
REQUIRE(buf[2] == '\0');

Add support for other standard length modifiers

The C99 spec defines the following additional modifiers:

hh     A following integer conversion corresponds to a signed char or 
       unsigned char argument, or a following n conversion corresponds
       to a pointer to a signed char argument.
h      A following integer conversion corresponds to a short int or unsigned
       short int argument, or a following n conversion corresponds to a
       pointer to a short int argument.
L      A following a, A, e, E, f, F, g, or G conversion corresponds to a
       long double argument.
j      A following integer conversion corresponds to an intmax_t or
       uintmax_t argument, or a following n conversion corresponds to a
       pointer to an intmax_t argument.
t      A following integer conversion corresponds to a ptrdiff_t argument,
       or a following n conversion corresponds to a pointer to a ptrdiff_t
       argument.

It would be good to support those for people that want a standard-compliant printf replacement.

h and hh should be relatively straightforward, as char and short decay to int in varargs; so they should just be accepted, but do nothing.

snprintf() and vsnprintf() return value comment

Hi, just a tiny remark: the comment for snprintf/vsnprintf seems to be incorrect:

(returns) The number of characters that are WRITTEN into the buffer, not counting the terminating null character. If the formatted string is truncated the buffer size (count) is returned

The description in the readme file is ok:

Anyway, if the output was truncated due to this limit, the return value is the number of characters that could have been written. Notice that a value equal or larger than count indicates a truncation.

I made a quick update so I'll place a PR (I didn't find the -1 being returned from anywhere in case of errors, so I didn't mention it explicitly).

Discard round-to-even code?

Round-to-even in %f is not working correctly, maybe it would be best to simply discard it completely? Opinions?

  unsigned long whole = (unsigned long)value;
  double tmp = (value - whole) * pow10[prec];
  unsigned long frac = (unsigned long)tmp;
  diff = tmp - frac;

The problem is that tmp value is multiplied by pow10.

With %.1f, 0.95:
0.95 is represented as 0.94999999999999996 in binary. This should be rounded down.
But (0.95 - 0) * 10 is 9.5, exactly representable in binary and tie-decision code takes place, rounding to even and outputting "1.0".

It may be possible to fix this double-rounding (at least in some cases), but IMO it's not worth the complexity. It may be simpler to just remove the tie-handling code and live with it

Why did you rename the functions to have an appended _?

Just curious about the motivation behind the name change (to have functions appended with an underscore, and then redefined without the underscore).

#define printf printf_
int printf_(const char* format, ...);

This caused some problems with my library when I updated. I have a libc which I use for embedded systems, but on host machines I link against the standard printf. With the new naming convention, I can no longer keep the printf.h header included and link against the std library - printf_ is undefined, since the host library defines printf.

fctprintf & null termination

In using the fctprintf printf function, I notice that the NULL terminator is passed to the out function. In my experience, printing into a buffer should result in a NULL-terminator, but printf itself does not print the NULL byte.

My personal opinion is that fctprintf should match the printf behavior (updated in #19). Curious about your thoughts on the matter.

Warning: comparing floating point with == or != is unsafe [-Wfloat-equal]

Compiling with GCC using -Wfloat-equal turned on, I get the following warnings:
printf.c:346:18: warning: comparing floating point with == or != is unsafe [-Wfloat-equal]
printf.c:363:20: warning: comparing floating point with == or != is unsafe [-Wfloat-equal]

The code does has a compare to 0.5

Any ideas on how to resolve this?
Thank you for creating this wonderful function!

floating point zero handling

When printing "%e" or "%g" with zero as argument, 0e-308 is assumed instead of zero

printf("%e", 0):
expected: "0.000000e+00"
got: "0.000000e-308"

printf("%g", 0):
expected: "0"
got: "0.000000e-308"

Rename functions and use macros to avoid conflicts with STD

In printf.h you would have something like this:

void x_putchar(char character);
int x_printf(const char* format, ...);
int x_sprintf(char* buffer, const char* format, ...);
int x_snprintf(char* buffer, size_t count, const char* format, ...);
int x_vsnprintf(char* buffer, size_t count, const char* format, va_list va);
int x_fctprintf(void (*out)(char character), const char* format, ...);

Along with a set of macros:

#define vsnprintf x_vsnprintf
#define snprintf x_snprintf
...

This is what tinyprintf does and it is very convenient because it avoids conflicts with the STD printf functions.

I'm actually coming from tinyprintf because it lacks float support and doesn't seem to be updated often.

%g should omit trailing zeros

printf("%g", 0.0001) : 
expected:  "0.0001"
got: "0.000100000" 

printf("%g", 0.00001) : 
expected: "1e-05"
got : "1.000000e-05"

printf("%#.5o", 06143), printf("%#.0o", 0)

In this case (and wider precision) octal zero should be included in padding instead of adding it

printf("%#.5o", 06143)
expected: 06143
got: 006143

printf("%#.6o", 06143)
expected: 006143
got: 0006143

another partially-related case is

sprintf("%#.0o", 0)
glibc: "0"
got: ""

Return value off by one

The return value should return the number of bytes written, without the terminating zero (as mentioned by the printf spec and your source code comments).
Although, the following returns 10, instead of 9:

char buf[10];
int size = snprintf(buf, 10, "hello, world");
/* size is 10, but should be 9. */

printf appends null terminator to formatted output

Hello,
Thank you for the fantastic work.
Should printf append a null terminator at the end of the formatted output? The standard seems to be unclear about this. The only statement I could find in the c11 standard regarding this is "The fprintf function returns when the end of the format string is encountered." [§7.21.6.1.2] (according to the standard, printf is equivalent to fprintf except for the first argument).

Not standard on prefix + precision

printf("%#08x %#0.8x\n",0x614e,0x614e);

// currently:
0x00614e 0x00614e

// standard;
0x00614e 0x0000614e

(edited to show same bad behaviour, but with precision <= 9 instead of 10)

Suggestion: vprintf()

Hello,

I think you could add a vprintf implementation as below. I've done this for my libc, but feel free to incorporate it on your end. (vprintf is required by libc++)

int vprintf(const char *restrict format, va_list va)
{
	char buffer[1];
	return _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
}

Bug in # flag

sprintf("%#4x", 0x1234); produces `"0x34"
It is caused by wrong '0x' space compensation when width equals output

BTW: I'm fixing issues reported, no need to work on it immediately

Scawy big no functionality at all UwU

Senpai,

your little kawaii pwintf impwementation is getting out of contwol.
Pwease have a look at this:

The pwintf function:
int printf(const char* format, ...)
{
  va_list va;
  va_start(va, format);
  const int ret = _vsnprintf(_out_char, NULL, (size_t)-1, format, va);
  va_end(va);
  return ret;
}

The _vsnpwintf function:
static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va)
{
  unsigned int flags, width, precision, n;
  size_t idx = 0U;

  if (!buffer) {
    // use null output function
    out = _out_null;
  }
...

Since we pass null fow the buffer, the output function is being set to _out_null,
which doesn't do anything. Pwintf has ceased functioning entirely.

Oh no!

Problems with zero precision

There are some problems with a precision of 0 (an annoying corner case).

The following crash:

  • printf("% .0d", 0); (should output )
  • printf("%+.0d", 0); (should output +)

This is caused by decrementing a 0 len here: https://github.com/mpaland/printf/blob/master/printf.c#L185

printf("%#.0x", 0); is apparently supposed to output an empty string, not 0x. The octal case is correct, though. It seems this was clarified in C11.

line feed for printf("\n some text")

Hi,

Today I made at first test on my stm32 board with your lib. First impression works great an compiles without any warning on gnu_arm_gcc 5.x

One usage question... normaly I use :

printf("\nHallo Welt");

if I would like to print something at the beginning of a new line. But with your implementation I do also need some additional line feed - otherwise the line does not start at position 0 on a terminal:

printf("\n\rHallo Welt");

So I am wondering if the _printchar should handles this is the printf implementation responsible for that?

many thanks

hexa and 0

Hi,

It seems that printf("%#x", 0) outputs 0 instead of 0x0.

Is that normal ?

Thanks !

Scawy big unkown symbol UwU

Hii~~

This time the types are all right and pwoper,
but nullptr isn't anything in C.

Can we have NULL pwease?

printf.c: In function 'printf':
printf.c:650:41: error: 'nullptr' undeclared (first use in this function)
const int ret = _vsnprintf(_out_char, nullptr, (size_t)-1, format, va);

Floats : out of range

Hi,

What is missing exactly to support double numbers ?

I see the message in the tests :

printf/test/test_suite.cpp

Lines 1016 to 1017 in 21a282a

// out of range in the moment, need to be fixed by someone
test::sprintf(buffer, "%.1f", 1E20);

Maybe for the moment, a message can be added to the caveats in the README :

Double precision is not supported and will result in an empty string.

Thanks !

C++ header only version

Hi, excellent lib, clean and sober code, really good this is. Now.
I am wondering if you could consider cpp header only version?
Yes, I know that would be a "point-of-no-return" to C ..

possible `%g` minor non-compliance

There is minor problem with %g specifier:

from https://www.gnu.org/software/libc/manual/html_node/Floating_002dPoint-Conversions.html#Floating_002dPoint-Conversions

The ‘%g’ and ‘%G’ conversions print the argument in the style of ‘%e’ or ‘%E’ (respectively) if the exponent would be less than -4 or greater than or equal to the precision; otherwise they use the ‘%f’ style. A precision of 0, is taken as 1. Trailing zeros are removed from the fractional portion of the result and a decimal-point character appears only if it is followed by a digit.

printf("%0-15.3g", -42.); :

"-42.0          "
glibc printf:
"-42            "

It seems to me that %g should use shortest representation, precision is only upper limit, # modifier may be used if decimal point is requested (unimplemented now)
(https://stackoverflow.com/questions/30658919/the-precision-of-printf-with-specifier-g)

printf("%s", NULL) - crash

Hi!

Some placeholder should be used for NULL %s argument to prevent printf from crash:

      case 's' : {
        const char* p = va_arg(va, char*);
        if (!p) p = "(null)"; // please add this

Use of sizeof() operator

Repeating yourself is generally bad and can easily lead to bugs when updating software. I suggest to change code in functions like _ntoa_long_long:

char buf[PRINTF_NTOA_BUFFER_SIZE];
....
do { 
...
} while (len < PRINTF_NTOA_BUFFER_SIZE);

to:

char buf[PRINTF_NTOA_BUFFER_SIZE];
....
do { 
...
} while (len < (sizeof(buf) / sizeof(buf[0])));

or if typing twice sizeof is too much, you may do:

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

and then

} while (len < ARRAY_SIZE(buf));

Specially important if this is automotive ready code.

Caveat: requires remainder, divide, and multiply instructions or software implementations

This came from this reddit thread https://web.archive.org/web/20200109232034/https://www.reddit.com/r/RISCV/comments/eme6qb/gcc_error_undefined_reference_to_umodsi3/

If the computer architecture does not include remainder, divide, or multiply instructions or software implementations this does not compile. Such instance would be a processor that only implements rv32I instruction set as seen in the reddit thread.

Support for %zd

On latest libc's the %zd specifier can be used to print size_t.

need lib : stdio stdarg in the include code

Dear mpaland,
hi, i am using ur printf in Arm process and RiscV lib, to get the "printf " function with mimimum lib file
but i cannot get the stdio and stdarg in your code; so far as too many stdio and starg versions : MS VC6.0 /VC2.0 and so on.
so i'd like to get stdio/stdarg from the your repo in github
thank you!

`fctprintf` needs void pointer argument

For fctprintf to be more useful, it would be great if it had a void pointer argument to customize what the given callback function actually relates to, e.g. with regard to a specific stream instance.

Right now, only a statically defined function can be passed to fctprintf which must behave identical (bar global variables which would defeat the whole purpose of having the callback) each call.

Think about the following signature and call examples:

int fctprintf(void (*out)(char character, void *user), void *user, const char *format, ...);

struct Stream
{
  // ...
};

void do_out(char character, void *user)
{
  Stream *stream = (Stream *)user;
  // Output character on `stream`.
}

Stream stream_1;
Stream stream_2;

fctprintf(do_out, (void *)&stream_1, "%s", "Hello 1");
fctprintf(do_out, (void *)&stream_2, "%s", "Hello 2");

%p format

For consideration: Maybe it would be useful to change %p format to be compatible with libc:

In the GNU C Library, non-null pointers are printed as unsigned integers, as if a ‘%#x’ conversion were used. Null pointers print as ‘(nil)’. (Pointers might print differently in other systems.)

Or make this compile-time configurable ...

Passing negative precision is broken

An indirect precision (as in .*) is passed as a signed int. But this code interprets it as unsigned. C11 even specifies that negative precision is taken as if the precision were omitted.

Test case: printf("%.*d", -1, 1) should output 1.

Incorrect test case

The "float" test suite contains the following assertions:

test::sprintf(buffer, "%.9f", 42.8952);
REQUIRE(!strcmp(buffer, "42.895200000"));

test::sprintf(buffer, "%.10f", 42.895223);
REQUIRE(!strcmp(buffer, "42.895223000"));

The first assertion looks correct, but the second assertion appears to be wrong: the precision is set to 10 and there are only 9 digits after the decimal point.

I've checked this with GLIBC 2.23-0ubuntu10 and indeed, 10 digits appear after the decimal point.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.