EEVblog Electronics Community Forum

Products => Computers => Programming => Topic started by: HwAoRrDk on October 18, 2022, 02:49:42 pm

Title: printf: what exactly should 'h' be for 16-bit ints?
Post by: HwAoRrDk on October 18, 2022, 02:49:42 pm
Due to the excessive size of my platform's standard library printf implementation, and being increasingly short on space, I'm writing my own very basic one.

I'm unsure about what exactly to do with the 'h' length modifier for integer format specifiers (e.g. %u, %d, %x, etc.) when the native word size for my 8-bit platform is 16-bits - that is, int = uint16_t, and shortint.

Now, according to the reference on cppreference.com (https://en.cppreference.com/w/c/io/fprintf), the 'h' length modifier denotes a short value, and indeed my platform's standard library implementation of printf ignores 'h' (because it would cause no difference in behaviour to int). But I've also seen other printf implementations for 8-bit platforms that treat 'h' as char. I suppose that would make sense if you interpret 'h' as meaning 'half', and char is indeed 'half' of int.

What to do? :-//
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: DavidAlfa on October 18, 2022, 03:06:00 pm
Try sizeof(short) to see the size on your architecture?
Ultimately check the compiler manual, it's usually there.
For example, XC8 use the same values for 8 and 16bit integers.
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: HwAoRrDk on October 18, 2022, 03:13:35 pm
Try sizeof(short) to see the size on your architecture?

I already know what size short is. As I already said, sizeof(short) = sizeof(int) - both are 2 bytes.
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: DavidAlfa on October 18, 2022, 03:16:00 pm
Anyways, %h must be very rare, never saw that, I think a lot of compilers only support %d, %u, then l, ll..
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: HwAoRrDk on October 18, 2022, 05:16:18 pm
Hmm, I just remembered that with variadic functions (such as printf), all non-fixed integer arguments undergo integer promotion. So, everything that is smaller than an int gets promoted to that. Now I see why in my platform's standard library printf implementation, it ignores the 'h' length modifier, because it won't ever get passed anything that is smaller than int.

So why does the 'h' or 'hh' length modifiers even exist? ???
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: DavidAlfa on October 18, 2022, 06:05:45 pm
Legacy, perhabs? From the times you would sell organs for 64K ram?
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: coppice on October 18, 2022, 06:12:43 pm
Hmm, I just remembered that with variadic functions (such as printf), all non-fixed integer arguments undergo integer promotion. So, everything that is smaller than an int gets promoted to that. Now I see why in my platform's standard library printf implementation, it ignores the 'h' length modifier, because it won't ever get passed anything that is smaller than int.

So why does the 'h' or 'hh' length modifiers even exist? ???
Most of the %<something> entries for printf control strings are a horrible historical mess. The standards now define things like PRIu16 to be used in those strings, so obtain portable results. They work, but they can make the string really long and clumsy to read.

"h" comes from the very first edition of K&R in the 1970s, to print a "short int". The snag is they never specified what a short int is, and it varies from platform to platform.
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: DavidAlfa on October 18, 2022, 06:44:33 pm
I recall seeing a 24 bit "short long" type... But never the "%h"
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: TheCalligrapher on October 19, 2022, 08:29:56 am
Now, according to the reference on cppreference.com (https://en.cppreference.com/w/c/io/fprintf), the 'h' length modifier denotes a short value, and indeed my platform's standard library implementation of printf ignores 'h' (because it would cause no difference in behaviour to int).

OK. Sounds fine so far.

But I've also seen other printf implementations for 8-bit platforms that treat 'h' as char. I suppose that would make sense if you interpret 'h' as meaning 'half', and char is indeed 'half' of int.

That would be non-conforming. `h` means short, period. A conforming C implementation is not allowed to implement 8-bit `short`. The minimum range for `short` is -32767...+32767, which means that it is 16-bit wide at least.

Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: coppice on October 19, 2022, 05:21:43 pm
Now, according to the reference on cppreference.com (https://en.cppreference.com/w/c/io/fprintf), the 'h' length modifier denotes a short value, and indeed my platform's standard library implementation of printf ignores 'h' (because it would cause no difference in behaviour to int).

OK. Sounds fine so far.

But I've also seen other printf implementations for 8-bit platforms that treat 'h' as char. I suppose that would make sense if you interpret 'h' as meaning 'half', and char is indeed 'half' of int.

That would be non-conforming. `h` means short, period. A conforming C implementation is not allowed to implement 8-bit `short`. The minimum range for `short` is -32767...+32767, which means that it is 16-bit wide at least.
That depends on what you expect to conform with. If you go back to K&R, they didn't exclude a short from being 8 bits. They kept word lengths really vague, like they had looked at Pascal, and thought "let's be as messed up as that". There were numerous compilers for 8 bit machines in the past which implemented 8 bit short ints.
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: TheCalligrapher on October 19, 2022, 07:48:11 pm
That depends on what you expect to conform with.

By default you always conform with the latest language standard. Conforming withg something else (K&R, C89/90, C++98, whatever) is perfectly fine, but has to be declrated clearly, loudly and and explicitly.

Since the OP does not make a mention of a specific version of language spec, they are talking about C17.

If you go back to K&R, they didn't exclude a short from being 8 bits.

`printf` in K&R did not have such length specifiers as `h`. Since the OP is talking about `h`, it automatically means C99 or higher.
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: coppice on October 19, 2022, 08:04:28 pm
That depends on what you expect to conform with.

By default you always conform with the latest language standard. Conforming withg something else (K&R, C89/90, C++98, whatever) is perfectly fine, but has to be declrated clearly, loudly and and explicitly.

Since the OP does not make a mention of a specific version of language spec, they are talking about C17.
That is a very strange concept. Old code doesn't go away when new specifications arise. New specifications which actually break existing things are a disgrace. A lesson the C++ people never learned, but which the C people have generally been very cognisent of.
Quote
If you go back to K&R, they didn't exclude a short from being 8 bits.

`printf` in K&R did not have such length specifiers as `h`. Since the OP is talking about `h`, it automatically means C99 or higher.
I just checked and you are right. K&R only defined h for scanf, not for printf. They don't seem to mention how they expect you to print a short int, although its easy to cast it to an int. In practice, I can't remember a printf function that didn't support h for both printf and scanf.
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: HwAoRrDk on October 19, 2022, 08:40:38 pm
Since the OP does not make a mention of a specific version of language spec, they are talking about C17.

Yes, I am working with C17.

I just checked and you are right. K&R only defined h for scanf, not for printf. They don't seem to mention how they expect you to print a short int, although its easy to cast it to an int. In practice, I can't remember a printf function that didn't support h for both printf and scanf.

Talking of scanf, after doing a bit more research, I came to the conclusion that the 'h' (and 'hh') length modifiers must only be useful for scanf, and their presence for printf is really only for 'symmetry' rather than anything useful (due to all printf integer varargs being cast to int at minimum).



I have decided to ignore 'h' length modifiers in my implementation - it will get treated the same as int.

Have managed to save over 900 bytes by cutting out the standard library implementation and using my own! ;D :-+
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: ejeffrey on October 19, 2022, 11:06:26 pm
Now, according to the reference on cppreference.com (https://en.cppreference.com/w/c/io/fprintf), the 'h' length modifier denotes a short value, and indeed my platform's standard library implementation of printf ignores 'h' (because it would cause no difference in behaviour to int). But I've also seen other printf implementations for 8-bit platforms that treat 'h' as char. I suppose that would make sense if you interpret 'h' as meaning 'half', and char is indeed 'half' of int.

What to do? :-//

Treating %h as short (keeping in mind the actual argument will be promoted to int or unsigned int) is the only compatible/conforming things to do.  That said, since you are re-implementing printf from scratch because your library version is too bloated I assume your implementation will be non-conforming in several other ways.  At that point who cares?  Just name your function printfish and call it a day.
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: SiliconWizard on October 19, 2022, 11:16:10 pm
Well of course. The OP is implementing their own version for their own use. Why would they even bother with odd format specifiers they may never have used and which add practically no value anyway?

Were the OP commissioned to write a fully std-compliant printf, sure. But otherwise?

Now if you want to treat integers as a specific width N, given that the rule is integer promotion, just pass the value modulo 2^N - if the actual parameter is wider than the format. If you pass a short and you want it displayed as a short, %d (or %u for unsigned) will work just fine. Only if you pass say an int (which *may* be wider than a short on a given platform) or a long and want to format it as a short, will you need to pass the modulo. Use the % operator rather than a bitwise mask in this case if the integer is signed to properly keep the sign.


Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: golden_labels on October 19, 2022, 11:32:25 pm
Now, according to the reference on cppreference.com (https://en.cppreference.com/w/c/io/fprintf), the 'h' length modifier denotes a short value, and indeed my platform's standard library implementation of printf ignores 'h' (because it would cause no difference in behaviour to int). But I've also seen other printf implementations for 8-bit platforms that treat 'h' as char. I suppose that would make sense if you interpret 'h' as meaning 'half', and char is indeed 'half' of int.
printf is a variadic function. Integers smaller than int can’t be passed to it. h length modifier affects formatting, not argument type: it simply trims contents of the int passed to what short could represent.
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: TheCalligrapher on October 20, 2022, 03:30:41 am
I just checked and you are right. K&R only defined h for scanf, not for printf. They don't seem to mention how they expect you to print a short int, although its easy to cast it to an int. In practice, I can't remember a printf function that didn't support h for both printf and scanf.

Um... It is not the same thing.

K&R defined `h` as a full-blown standalone conversion specifier, i.e you were supposed to use it as `%h` in `scanf`.

ANSI C abandoned `h` as a conversion specifier completely. There's no such thing as `%h` in standard C. Neither in `scanf` nor in `printf`. Niether in the first C89/90 nor in modern C. K&R's `%h` is gone for good.

C99 introduced `h` in a very different role: as a length modifier, not as a standalone format specifier. Since C99 `h` is supposed to be used in conjunction with "real" format specifiers like `d` or `u`, e.g. `%hd`.

So, the role `h` played in K&R is very different from the role it plays in C99 and later. The OP's question is about the latter.
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: Ed.Kloonk on October 20, 2022, 06:24:46 am
This is why hybrid languages annoy me.

The .0001% use case where C implementation is haywire and coders just crack the shits and reinvent the wheel instead of commenting the life out of the one bit of problematic code so that we can sort it out when it matters. Sorry. /rant
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: newbrain on October 20, 2022, 08:22:53 am
K&R defined `h` as a full-blown standalone conversion specifier, i.e you were supposed to use it as `%h` in `scanf`.

ANSI C abandoned `h` as a conversion specifier completely. There's no such thing as `%h` in standard C. Neither in `scanf` nor in `printf`. Niether in the first C89/90 nor in modern C. K&R's `%h` is gone for good.

C99 introduced `h` in a very different role: as a length modifier, not as a standalone format specifier. Since C99 `h` is supposed to be used in conjunction with "real" format specifiers like `d` or `u`, e.g. `%hd`.

So, the role `h` played in K&R is very different from the role it plays in C99 and later. The OP's question is about the latter.
Minor correction: as a length modifier, %h<<conversion specifier>>, is to my knowledge* already there in C90.
What is actually new in C99 is the 'hh' length modifier.
This makes sense, otherwise there would be no way to use a short integer in a scanf, removing useful functionality from K&R C.

* Unfortunately, I only have copies of the standard starting from C99 - where 'h' is neither listed in the changes wrt C90, nor it is in the rationale.
But I have K&R books, from the first edition, confirming h was a conversion specifier:
Quote
The conversion character indicates the interpretation of the input field; the corresponding argument must be a pointer, as required by the call by value semantics of C. The following conversion characters are legal:
[...]
h a short integer is expected in the input; the corresponding argument should be a pointer to a short integer.
From the second, ANSI, edition:
Quote
The conversion characters d, i, o, u, and x may be preceded by h to indicate that a pointer to short rather than int appears in the argument list [...]
Title: Re: printf: what exactly should 'h' be for 16-bit ints?
Post by: TheCalligrapher on October 20, 2022, 04:29:56 pm
Minor correction: as a length modifier, %h<<conversion specifier>>, is to my knowledge* already there in C90.

Yes, you are right. Somehow I missed it. As a length modifier `h` is already present in C89/90 for both `printf` and `scanf`.