home bbs files messages ]

Forums before death by AOL, social media and spammers... "We can't have nice things"

   comp.lang.c      Meh, in C you gotta define EVERYTHING      243,242 messages   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]

   Message 242,089 of 243,242   
   David Brown to bart   
   Re: _BitInt(N)   
   24 Nov 25 14:49:08   
   
   From: david.brown@hesbynett.no   
      
   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.)   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]


(c) 1994,  bbs@darkrealms.ca