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,186 of 243,242   
   David Brown to bart   
   Re: _BitInt(N)   
   27 Nov 25 14:39:51   
   
   From: david.brown@hesbynett.no   
      
   On 27/11/2025 13:46, bart wrote:   
   > On 27/11/2025 02:32, Waldek Hebisch wrote:   
   >> bart  wrote:   
   >>>   
   >>> This is about a lower-level systems language working with primitive   
   >>> machine types, and having access to the underlying bits of those types.   
   >>>   
   >>> How much more fundamental can you get?   
   >>>   
   >>> C provides only basic bitwise operators, and you have to do some   
   >>> bit-fiddling, while trying to avoid UB, in order to extract or inject   
   >>> individual bits or bitfields.   
   >>>   
   >>> I provide direct indexing ops to get or set any bit or bitfield, which   
   >>> is actually a great core feature to have, but for some reason you want   
   >>> to downplay it.   
   >>>   
   >>> You might just admit for once that it is quite neat.   
   >>   
   >> Yes, it is neat.   
   >   
   > Hmm, perhaps you're being sincere, perhaps not ...   
   >   
   >>> OK, so how would you do a 'reinterpret' cast in C, of a value like   
   >>> 'x+y'?   
   >> #include    
   >> #include    
   >>   
   >> uint64_t   
   >> d_to_u(double d) {   
   >>      uint64_t tmp;   
   >>      memcpy(&tmp, &d, sizeof(tmp));   
   >>      return tmp;   
   >> }   
   >>   
   >> int   
   >> f_exp(double d) {   
   >>     return (d_to_u(d)>>52)&2047;   
   >> }   
   >>   
   >> Using 'gcc -O' I get the following assembly (only code, without   
   >> unimportant directives/labels):   
   >>   
   >> d_to_u:   
   >>          movq    %xmm0, %rax   
   >>          ret   
   >>   
   >> f_exp:   
   >>          movq    %xmm0, %rax   
   >>          shrq    $52, %rax   
   >>          andl    $2047, %eax   
   >>          ret   
   >>   
   >> As you can see 'd_to_u' is single computational instruction,   
   >> you can not do better given that floating point registers   
   >> are distinct from integer registers.  And 'f_exp' looks   
   >> optimal assuming lack of "bit extract" or "extract exponent"   
   >> instructions.   
   >>   
   >> Note that you can put both functions above in a header file,   
   >> so once you have written few lines above you can use them   
   >> in all your C code.  Of course, efficientcy depends on   
   >> compiler optimization.   
   >   
   > Yes (that's something I can't rely on).   
   >   
   > These examples are interesting: with a HLL you normally express yourself   
   > in a clear manner, and it is the compiler's job to generate the   
   > complicated code required to implement what you mean.   
   >   
   > Here it seems to be other way around: it is the programmer who writes   
   > the convoluted code, and the compiler turns that into short, clear   
   > instructions! Which unfortunately no one will see.   
   >   
      
   I don't think Waldek's code (or mine) is particularly convoluted.  But   
   in either case, you put such things in static inline functions (or   
   macros if you need to).  Then you have clear intent when implementing   
   those functions - you are clearly doing low-level shifts and masking.   
   And you have clear intent when /using/ the functions - you are   
   extracting some bits from the underlying representation of the value.   
   You split things into identified functions with specific tasks - that's   
   at the heart of programming.   
      
   And then you let the automated computer system - the compiler - do what   
   it does best, and generate efficient results.   
      
   > If I use your functions like this:   
   >   
   >      a = f_exp(x + y);   
   >   
   > then once the x+y result is in a register, gcc-O2 generates this inline   
   > code for the extraction:   
   >   
   >      movq    rax, xmm0   
   >      shr rax, 52   
   >      and eax, 2047   
   >   
   >   
   > If I express it in my language:   
   >   
   >     a := int@(x + y).[52..62]   
   >   
   > then my non-optimising compiler generates this (D0 is rax):   
   >   
   >      movq      D0,    XMM4   
   >      shr       D0,    52   
   >      and       D0,    2047   
   >   
   > So such features have definite advantages, in being able to express   
   > intent directly, and to make it easier for a simple compiler to know   
   > that intent and help it generate reasonable code without lots of   
   > analysis or needing function inlining.   
   >   
      
   You seem to be arguing that it is a good thing to write code that   
   spoon-feeds the compiler so that the compiler doesn't have to do much   
   work.  You get this because you are writing the application code and   
   also writing the compiler - so you pick the solution that gives you the   
   best results for the least effort overall.  But that is only appropriate   
   for people with personal languages like yours.   
      
   It should be the other way round - the compiler should be optimising so   
   that the programmer can work at higher levels of abstraction or write   
   code in the way that is most convenient to them, and the compiler will   
   handle the boring low-level details.  Programmers using serious   
   languages /can/ rely on the compiler optimising well.   
      
      
   > BTW, your example explicitly writes to memory; David Brown posted a   
   > version that didn't do so that I could see. Unless a compound literal is   
   > designed to be built in memory? However that version only seemed to work   
   > with one compiler.   
      
   The version I wrote is C99 and works fine with any C99 compiler.  No,   
   compound literals are not "designed to be built in memory", whatever   
   that might mean.  A compound literal is a value, and can be used like   
   any other value.   
      
   Waldek's version does use "memcpy" and pointers formed from the   
   addresses of a parameter and a local variable.  That means it must give   
   results as if the parameter and local variable were in memory somewhere   
   (the stack, in usual practice - though C does not actually require a   
   stack) and a memory-to-memory copy was carried out, byte by byte.   
   Critical here is the term "as if".  If the compiler can give the same   
   results without using memory, it is allowed to do so - thus optimising   
   compilers will just do a register transfer from a floating point   
   register to a general purpose register in this case.   
      
   --- 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