From: antispam@fricas.org   
      
   David Brown wrote:   
   > On 24/11/2025 12:17, bart wrote:   
   >> On 24/11/2025 09:29, David Brown wrote:   
   >>> On 23/11/2025 16:06, Michael S wrote:   
   >>>> On Sun, 23 Nov 2025 13:59:59 +0000   
   >>>> bart wrote:   
   >>   
   >>>>> So what is the result type of multiplying values of those two types?   
   >>>>>   
   >>>>   
   >>>> I think, traditional C rules for integer types apply here as well: type   
   >>>> of result is the same as type of wider operand. It is arithmetically   
   >>>> unsatisfactory, but consistent with the rest of language.   
   >>>   
   >>> There is one key difference between the _BitInt() types and other   
   >>> integer types - with _BitInt(), there are no automatic promotions to   
   >>> other integer types. Thus if you are using _BitInt() operands in an   
   >>> arithmetic expression, these are not promoted to "int" or "unsigned   
   >>> int" even if they are smaller (lower rank). If you mix _BitInt()'s of   
   >>> different sizes, then the smaller one is first converted to the larger   
   >>> type.   
   >>   
   >>>> I think, the Standard is written in such way that implementing _BitInt   
   >>>> as an arbitrary precision numbers, i.e. with number of bits held as part   
   >>>> of the data, is not allowed.   
   >>   
   >>> Correct. _BitInt(N) is a signed integer type with precisely N value   
   >>> bits. It can have padding bits if necessary (according to the target   
   >>> ABI), but it can't have any other information.   
   >>>   
   >>>> Of course, Language Support Library can be   
   >>>> (and hopefully is, at least for gcc; clang is messy a.t.m.) based on   
   >>>> arbitrary precision core routines, but the API used by compiler should   
   >>>> be similar to GMP's mpn_xxx family of functions rather than GMP's   
   >>>> mpz_xxx family, i.e. # of bits as separate parameters from data arrays   
   >>>> rather than combined.   
   >>>>   
   >>>   
   >>> Yes, exactly. At the call site, the size of the _BitInt type is   
   >>> always a known compile-time constant, so it can easily be passed on.   
   >>> Thus :   
   >>>   
   >>> _BitInt(N) x;   
   >>> _BitInt(M) y;   
   >>> _BitInt(NM) z = x * y;   
   >>   
   >> So what is NM here; is it N*M (the potential maximum size of the   
   >> result), or max(N, M)?   
   >   
   > No, it is whatever you want it to be. I didn't want to use the next   
   > letter after N because _BitInt(O) could easily be misunderstood. But of   
   > course NM could be misunderstood too. Perhaps N1, N2 and N3 would have   
   > been better choices than N, M and NM.   
   >   
   > You pick the size of "z" here according to your needs for your code.   
   > The multiplication will be done, logically, at max(N, M) bits. The   
   > result will then be converted to NM bits. Like always in C, the   
   > semantics of the calculation is entirely independent of the type of the   
   > variable you assign the results to. And like always in C, the compiler   
   > may take advantage of knowledge of the assigned type in order to give   
   > more efficient code, as long as it does not stray from giving the same   
   > value as if it took the code literally.   
   >   
   > 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.   
   >   
   > 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.   
   >   
   > 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.   
   >   
   > If you want generics, you are better off with a language that supports   
   > generics, such as C++.   
   >   
   >> Even if you passed the size as a parameter, there would be a problem   
   >> with the BitInt type.   
   >   
   > Yes. But you could use a void* pointer for more generic parameters.   
   >   
   > However, _BitInt types are for "bit-precise integer types". They are   
   > for specific fixed sizes, not for arbitrary precision integers. They   
   > are not ideally suited for tasks for which they were not designed -   
   > that's hardly surprising.   
   >   
   >>   
   >> 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.)   
   >   
   >>   
   >> This sets it apart from arrays, where you also define very large, fixed   
   >> size arrays, but can use a T(*)[] type to write generic functions, that   
   >> take an additional length parameter.   
   >   
   > _BitInt's are fixed-size integer types, not arrays. Again, it is not   
   > then surprising that they are different from arrays.   
   >   
   >>   
   >> This will be for a particular T, but for BitInt, T is also fixed; it   
   >> happens to be an implicit bit type.   
   >>   
   >   
   > _BitInt's are not arrays, they are scalars - they are integer types.   
   > There is no concept of a type "_BitInt" - they always have compile-time   
   > fixed sizes, such as "_BitInt(12)". So the idea of passing around   
   > generic _BitInt's makes no more sense than passing around any other kind   
   > of generic integer types. (Of course you can have an array of _BitInt's   
   > of any given size.)   
      
   There are languages which pass generic types, but C is not one   
   of them. So idea of passing around generic _BitInt's makes sense,   
   but this is not included in C.   
      
   --   
    Waldek Hebisch   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|