Path: csiph.com!eternal-september.org!feeder.eternal-september.org!nntp.eternal-september.org!eternal-september.org!.POSTED!not-for-mail From: Keith Thompson Newsgroups: comp.lang.c Subject: Re: _BitInt(N) Date: Mon, 24 Nov 2025 17:23:47 -0800 Organization: None to speak of Lines: 91 Message-ID: <87h5ui3nx8.fsf@example.invalid> References: <10dajlh$ko3c$1@dont-email.me> <10fus62$hl69$1@solani.org> <10fv2dm$3can9$1@paganini.bofh.team> <10fv40v$1f7a2$1@dont-email.me> <20251123170654.000056a9@yahoo.com> <10g18hq$28nc2$1@dont-email.me> <10g1erh$2b2cf$1@dont-email.me> <10g1nol$2f8lb$1@dont-email.me> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Injection-Date: Tue, 25 Nov 2025 01:23:48 +0000 (UTC) Injection-Info: dont-email.me; posting-host="fa7a31f1160d6b9aea861ca62102f1fd"; logging-data="3113762"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18KSvAHvRlYNkK7I2nXgQvU" User-Agent: Gnus/5.13 (Gnus v5.13) Cancel-Lock: sha1:mPdIN0+85HIleq+H/QOvXL+COvI= sha1:jngHnmlvgw7lw2pcYuivJ8n9nVA= Xref: csiph.com comp.lang.c:395447 David Brown writes: > On 24/11/2025 12:17, bart wrote: >> On 24/11/2025 09:29, David Brown wrote: [...] > So if you want the full range of values of x and y to be usable here, > then NM would have to be N * M. But you would also need a cast, such > as "_BitInt(NM) z = (_BitInt(NM)) x * y;", just as you do if you want > to multiply two 32-bit ints as a 64-bit operation. N + M, not N * M. > Alternatively, you might know more about the values that might be in x > and y, and have a smaller NM (though you still need a cast if it is > greater than both N and M). Or you might be using unsigned types and > want the wrapping / masking behaviour. > > The point was not what size NM is, but that it is known to the > compiler at the time of writing the expression. > >> It sounds like the max precision you get will be the latter. >> >>> can be implemented as something like : >>> >>>      __bit_int_signed_mult(NM, (unsigned char *) &z, >>>              N, (const unsigned char *) &x, >>>              M, (const unsigned char *) &y); >>> >>> >> How would you write a generic user function that operates on any >> size BitInt? For example: >>    _BitInt(?) bi_square(_BitInt(?)); >> > > You can't. _BitInt(N) and _BitInt(M) are distinct types, for > differing N and M. You can't write a generic user function in C that > implements "T foo(T)" where T can be "int", "short", "long int", or > other types. C simply does not have type-generic functions. Sort of. C23 defines the term "generic function" (N3220 7.26.5.1, string search functions). For example, strchr() can take a const void* argument and return a const void* result, or it can take a void* argument and return a void* result. (C++ does this by having two overloaded strchr() functions.) These "generic functions" are (almost certainly) implemented as macros that use _Generic. If you bypass the macro definition, you get the function that can take a const char* and return a char*. So C doesn't have type-generic functions, but it does have feature that let you implement things that act like type-generic functions. > You /can/ write generic macros that handle different _BitInt types, > but that would quickly get painful given that you'd need a case for > each size of _BitInt you wanted for the _Generic macro. Indeed. A _Generic selection that handles all the ordinary non-extended integer types needs to handle 12 cases if I'm counting correctly, which is feasible. But the addition of bit-precise types adds BITINT_MAXWIDTH*2-1 new distinct predefined types, and a generic selection would need one case for each. However, you could have a function that takes a void*, a size, and a width as arguments and operates on a _BitInt(?) or unsigned _BitInt(?) type. In fact, gcc has internal functions like that for multiplication and division. (You mentioned something like that in text that I've snipped.) [...] >> This assumes BitInts are passed and returned by value, but even >> using BitInt* wouldn't help. > > Yes, they are passed around as values - they are integer types and are > passed around like other integer types. (Implementations may use > stack blocks and pointers for passing the values around if they are > too big for registers, just as implementations can do with any value > type. That's an implementation detail - logically, they are passed and > returned as values.) Yes, and in general a _BitInt argument has to be copied to the corresponding parameter, since a change to the parameter can't affect the value of the argument. But passing huge _BitInts by value is no more problematic than passing huge structs by value. [...] -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com void Void(void) { Void(); } /* The recursive call of the void */