3

I was wondering how you could printf _ExtInts in clang without using casts. Something like this:

#include <stdio.h>

int main() {
    _ExtInt(13) foo = 100;
    printf("%???", foo);
}

With casts, it would look like this (this is not what I want):

#include <stdio.h>

int main() {
    _ExtInt(32) foo = 100;
    printf("%d", (int) foo);
}
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
xilpex
  • 3,097
  • 2
  • 14
  • 45

2 Answers2

4

The _ExtInt types are a new feature in Clang (LLVM) as described at The New Clang _ExtInt Feature Provides Exact Bitwidth Integer Types, published 2020-04-21 (3 days ago as I type).

If _ExtInt(32) is a 32-bit signed integer type and int is a 32-bit signed integer type, then you can use %d and no cast in both calls to printf(). The arguments after the format are subject to the integer promotion rules, so I expect that both _ExtInt(13) and _ExtInt(32) would be converted to int as they are passed to printf(), so the correct conversion specifier is %d.

If you use bigger types, up to _ExtInt(64), you can probably use %lld on any machine (or %ld on a 64-bit machine). If you go bigger than that, you are on your own; you need an implementation of printf() that knows how to handle _ExtInt types, and that will probably have notations in the format that allow for the length to be specified. For example, hypothesizing wildly, it might support %<700>d for a signed _ExtInt(700).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 4
    [It appears that these extended integer types are not subject to integer promotion](https://clang.llvm.org/docs/LanguageExtensions.html#extended-integer-types) – ad absurdum Apr 25 '20 at 02:42
  • @exnihilo: But the document says "Unary `+`, `-`, and `~` operators typically will promote operands to int. Doing these promotions would inflate the size of required hardware on some platforms, so extended integer types aren’t subject to the integer promotion rules in these cases." That's fine; it's not clear to me that `printf()` is any of those operators, though. I imagine that the rules apply to the shift operators, and the bitwise operators too. It also says "Extended integer types align with existing calling conventions", which I take to mean "they work sanely with `printf()`" too. – Jonathan Leffler Apr 25 '20 at 02:49
  • I wondered at that too, but it didn't seem clear to me from the documentation. Yet the [page you linked to](http://blog.llvm.org/2020/04/the-new-clang-extint-feature-provides.html) also says: "_The _ExtInt types as implemented do not participate in any implicit conversions or integer promotions,...._" – ad absurdum Apr 25 '20 at 02:52
  • That's a blog page, and not as precise as what you found. And what you found doesn't cover all the operators sensibly — though it does specify that you have to have the same size on either side of an operator like `+`, so you can't add an `_ExtInt(8)` and an `_ExtInt(12)` without explicit casts. The "calling conventions" comment and surrounding text implies that `_ExtInt(13)` will be lengthened to an appropriate larger size (16 or 32 bits, probably — and I strongly suspect 32 bits for conventional chips, not the FGPA chips that it is targeted at). There is a lot left unsaid in both references. – Jonathan Leffler Apr 25 '20 at 02:57
  • If the function had a prototype that said `_ExtInt(21)` (for Unicode characters, for example), then you'd be able to pass an `_ExtInt(21)` to that function, and no other type. An `_ExtInt(16)` would not be implicitly promoted to `_ExtInt(21)`. But `printf()` and variadic functions like require types shorter than `int` to be converted to `int`. Either you can't pass `_ExtInt(21)` to `printf()` at all without a cast, or it gets promoted to `int` (presuming 32-bit `int`). I think that the "existing calling conventions" comment means you can. But I've not experimented with the real thing. – Jonathan Leffler Apr 25 '20 at 03:00
  • That makes sense, and you are probably right; it's just that I usually follow a "when in doubt, make an explicit conversion" policy ;) – ad absurdum Apr 25 '20 at 03:10
  • This seems good, but I'm not sure how to make custom printfs for those... – xilpex Apr 25 '20 at 03:26
  • @Xilpex: Neither am I — let me know when you find out how to do it. Hex is easy; decimal is not — at least, that's my immediate take on the topic. Maybe you do something like take the value modulo 1,000,000,000 and divide by the same to process 9 decimal digits at a time, using regular 32-bit `int` to deal with the value between 0 and 999,999,999. Of course, that generates the answer in reverse, so you have to worry about recursion, or something. Like I said, I don't know. – Jonathan Leffler Apr 25 '20 at 03:34
1

Use a wide type by forming a long product. int may be 16-bit. long is at least 32. long long is at least 64.

_ExtInt(13) foo1 = 100;
printf("%d\n", 1 * foo);
// or
printf("%d\n", 0 + foo);

_ExtInt(32) foo2 = 100;
printf("%ld\n", 1L * foo);
// or
printf("%ld\n", 0L + foo);
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 1
    This seems like a good way to avoid a cast; the extended integer types may not be subject to integer promotion, but they obey the usual arithmetic conversions. – ad absurdum Apr 25 '20 at 02:44
  • @Xilpex: you won't be able to process `_ExtInt(700)` with a conventional implementation of `printf()`. You will have to look at what the libraries provided support — there'll need to be special documentation. They may be waiting on the WG14 (standard C committee) to provide guidance. – Jonathan Leffler Apr 25 '20 at 03:12