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,098 of 243,242   
   David Brown to bart   
   Re: _BitInt(N) (1/2)   
   24 Nov 25 15:41:13   
   
   From: david.brown@hesbynett.no   
      
   On 24/11/2025 13:31, bart wrote:   
   > On 24/11/2025 11:17, David Brown wrote:   
   >> On 24/11/2025 01:30, bart wrote:   
   >   
   >>> Saving memory was mentioned. To achieve that means having bitfields   
   >>> that may not start at bit 0 of a byte, and may cross byte- or word-   
   >>> boundaries.   
   >>>   
   >>   
   >> No, that is incorrect.   
   >>   
   >> The proposal mentions saving /space/ as relevant in FPGAs - not saving   
   >> / memory/.   
   >   
   > But I was responding to a suggestion here that one use of _BitInts -   
   > presumably for ordinary hardware - was to save memory.   
   >   
      
   OK.  However, that is not what is in the proposal, nor in the C23 standard.   
      
   > That's not going to happen if they are simply rounded up to the next   
   > power-of-two type.   
      
   Correct (with the proviso that after 64 bits, rounding is to whatever   
   type can contain an int64_t).   
      
   As I mentioned, I don't think the C standards require that rounding-up   
   size, even though it was in the proposal.  It may be worth punting that   
   question over to the "comp.std.c" newsgroup to see if someone has a   
   definite answer.   
      
   For the kind of small systems that had been mentioned in the context of   
   saving memory, compilers often have extensions or   
   implementation-specific features (attributes, pragmas, etc.) to go   
   beyond standard C in order to get greater efficiency on tiny systems.   
   These may support smaller containers or tighter array packing.   
      
   >   
   > If the purpose is, say, a 17-bit type that wraps past values of 131071,   
   > then that sounds like a lot of extra code needed, for something that   
   > does not sound that useful. Why modulo 2**17; why not 100,000? Or any   
   > value more relevant to the task.   
   >   
      
   Signed _BitInt's don't wrap - arithmetic overflow is UB.  Unsigned   
   _BitInt's wrap, just like with all other unsigned integer types in C.   
   And wrapping is /not/ a lot of extra code - wrapping an N-bit type is   
   just a and instruction with the constant (2 << N) - 1.  This can be done   
   once at the end of complex arithmetic expressions, in most cases   
   (shift-right and division can mean extra masking is needed).   
      
   Why not provide wrapping types with arbitrary wrapping values?  Why not   
   indeed - some languages do (Ada springs to mind).  They are not actually   
   that often needed, so it's easier just to put a "% X" operation in the   
   user code.   
      
   The rational in the proposal that you linked said why these _BitInt   
   types can be useful.  They are for expressing the intent of the   
   programmer more clearly, making it more convenient to work with somewhat   
   bigger integer sizes (such as for cryptography), and improving FPGA   
   development.  Wrapping is not a big point (and it does not apply at all   
   to signed _BitInt).   
      
   >   
   >>   The authors use-case here is in writing code that can be compiled   
   >> with a "normal" C compiler on a "normal" target, and also compiled to   
   >> FPGA /hardware/, with the same semantics.  In hardware, a 5- bit by   
   >> 5-bit single-cycle multiplier is very much smaller than an 8-bit by   
   >> 8-bit multiplier, and orders of magnitude smaller than if the 5-bit   
   >> integers are promoted to 32-bit before multiplying.   
   >>   
   >> The proposal is not about saving /memory/.  It specifically says that   
   >> a _BitInt(N) has the same size and alignment as the smallest basic   
   >> type that can contain it, until you get to N greater than 64-bit, in   
   >> which they are contained in an array of int64_t.  (The reality is a   
   >> little more formal, to handle targets that have other sizes of their   
   >> basic types.)   
   >>   
   >> So on a "normal" target, a _BitInt(3) is the same size and alignment   
   >> as a uint8_t, a _BitInt(35) is effectively contained in an uint64_t,   
   >> and an array of 4 _BitInt(17) on a 32-bit system will take 16 bytes or   
   >> 128 bits, not 68 bits.   
   >   
   >> As far as I can see, the C23 standard does not specify these details,   
   >> and leaves them up to the target ABI.  But at the very least, they   
   >> will always take an integer number of bytes - unsigned char.  There   
   >> can never be any crossing of byte boundaries.   
   >   
   > What about arrays of _BitInt(1), _BitInt(2) and _BitInt(4)? These could   
   > actually be practically implemented, with a few restrictions, and could   
   > save a lot of memory.   
   >   
      
   They could - but that would add a lot of complications (the once you   
   worried about).  I would assume that this was considered both by the   
   authors of the proposal, and by the C committee, and rejected as not   
   being worth the cost.   
      
   >> Why?  And why do you talk specifically about odd numbers?  I can   
   >> understand your concern about packing arrays of _BitInts that are not   
   >> multiples of 8, though I hope you now understand that it is not the   
   >> problem you thought it was.  However, I see no reason to suppose that   
   >> _BitInt(5) is any more or less "complicated" than _BitInt(6) just   
   >> because 5 is an odd number!   
   >   
   > I mean odd compared with powers-of-two, or multiples of 8.   
      
   Okay.  "Unusual" might have been a better choice of term, or you could   
   have explained what you meant.  But that makes more sense.   
      
   >   
   >>   
   >> A major point of the _BitInt concept is to be able to specify and use   
   >> integers of specific explicit sizes in a way that is as implementation   
   >> independent as possible.  Some aspects of the implementation cannot be   
   >> avoided - such as the size of unsigned char and alignment and padding   
   >> for storage.  But the behaviour of the types is entirely independent   
   >> of the implementation.  There are no "extra rules" - neither for   
   >> specific implementations, nor for specific sizes of _BitInt's.   
   >>   
   >> Efficiency of implementation is, of course, up to the implementation.   
   >> But there is absolutely no reason to suppose that working with a   
   >> _BitInt of size up to the implementation's maximum integer type is   
   >> going to be less efficient than using other types and masking.  For   
   >> larger _BitInt's, there are different possible implementation   
   >> strategies with different pros and cons in regard to efficiency.   
   >>   
   >>>   
   >>> What happens when a 391-bit type, even unsigned, overflows? These   
   >>> larger types are likely to use a multiple of 64-bits, and for 391   
   >>> bits will need 7 x 64 bits, of which the last word will have 57 bits   
   >>> of padding. It's very messy.   
   >>>   
   >>   
   >> It is not messy at all.  Signed integer overflow is UB, unsigned   
   >> integer overflow is wrapping.  It's the same as always, and could not   
   >> be simpler, clearer or neater.   
   >   
   > In my 391-bit example, the top 7 bits will be within a 64-bit word. What   
   > values will those extra 57 bits be?   
   >   
      
   They are padding bits.  They don't contribute to the value of the object.   
      
   An implementation, or rather an ABI, can decide that they should always   
      
   [continued in next message]   
      
   --- 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