Frederick said:
Eric Sosman posted:
Hehe... I found the code so confusing that I didn't pay attention to
the comments.
You know, some of us do it the other way around
Anyhow, the macro is a stroke of genius. I wonder what other gems
Hallvard B Furuseth has come up with... ?
Not much if you mean C hacks. I just got so annoyed about the lack
of a log2 feature that I beat at it until something gave. For some
reason my most significant "hack" seems to be that one can use
struct align_TYPE_s { char c; TYPE align; };
... offsetof(struct align_TYPE_s, align) ...
instead of
sizeof(TYPE)
for the alignment of TYPE. I've sent that to the maintainers of
several programs where they cared about data size. I have no idea
why it's apparently not obvious.
A compile time "Log base 2" macro would be brilliant...
I gave that up since IMAX_BITS was hairy enough even when restricted to
(1<<k)-1 arguments. Silly of me. With a bunch of helper macros, it
works fine. Check me on this though, I've been staring at it too long:
/*
* Return (v ? floor(log2(v)) : 0) when 0 <= v < 1<<[8, 16, 32, 64].
* Inefficient algorithm, intended for compile-time constants.
*/
#define LOG2_8BIT(v) (8 - 90/(((v)/4+14)|1) - 2/((v)/2+1))
#define LOG2_16BIT(v) (8*((v)>255) + LOG2_8BIT((v) >>8*((v)>255)))
#define LOG2_32BIT(v) \
(16*((v)>65535L) + LOG2_16BIT((v)*1L >>16*((v)>65535L)))
#define LOG2_64BIT(v)\
(32*((v)/2L>>31 > 0) \
+ LOG2_32BIT((v)*1L >>16*((v)/2L>>31 > 0) \
Above this, shifting int/long by at most 15/31 bits per operation
gets cumbersome. Also the parentheses nesting level is already
rather high. But if the compiler doesn't mind approx 14K macro
expansions and lots of parentheses, even for simple macro arguments:
/*
* Return (v ? floor(log2(v)) : 0) when 0 <= v < 1<<512.
* Huge computation, intended for compile-time constants.
*/
#define LOG2_512BIT(val)\
LOG2_TMP_FINISH(val,\
LOG2_TMP_CHUNK(0, val,\
LOG2_TMP_CHUNK(1, val,\
LOG2_TMP_CHUNK(3, val,\
LOG2_TMP_CHUNK(7, val,\
LOG2_TMP_CHUNK(15, val,\
LOG2_TMP_CHUNK(31, val, 0)))))))
#define LOG2_TMP_FINISH(val, known_bits)\
(LOG2_8BIT((val) >> known_bits) + known_bits)
#define LOG2_TMP_CHUNK(ones, val, known_bits)\
(((val)*1L >>known_bits \
>>8>>ones>>ones>>ones>>ones>>ones>>ones>>ones>>ones \
> 0) * 8*(ones+1) + known_bits)
Still doesn't scale to "practically infinite" like IMAX_BITS does, but
I haven't seen a 1024-bit integer yet. (E.g. 4096 bits would need more
terms in LOG2_8BIT, 64 bit long long shifts, and 4 lines with >>ones.)
On the other hand - the computation doesn't have to be an _expression_.
By unpacking the above algorithm into intermediate enum constants, the
macro expansion is reduced from around 14K to around 0.8K. Also with
declarations we can get an error check by violating constraints if the
value is out of range:
/*
* Define Name = (val ? floor(log2(val)) : 0) as an enum constant.
* Expects 0 <= val < 1<<512. Tries to force a compile error otherwise.
* Note: Uses some <Name>__<letter/digit> identifiers.
*/
#define DEFINE_LOG2(Name, val)\
enum {\
Name##__5= LOG2_TMP_CHUNK(31, val, 0),\
Name##__4= LOG2_TMP_CHUNK(15, val, Name##__5),\
Name##__3= LOG2_TMP_CHUNK( 7, val, Name##__4),\
Name##__2= LOG2_TMP_CHUNK( 3, val, Name##__3),\
Name##__1= LOG2_TMP_CHUNK( 1, val, Name##__2),\
Name##__0= LOG2_TMP_CHUNK( 0, val, Name##__1),\
Name = LOG2_TMP_FINISH(val, Name##__0),\
Name##__c= (val)>>Name == !!(val) ? 1 : -999 \
};\
typedef struct{ int Name##__b:Name##__c; } Name##__t[Name##__c]