J
jacob navia
Continuing with my tutorial about C I have added a small text
about constant expressions.
Comments welcome.
--------------------------------------------------------------
Constant expressions
-------------------
The standard defines constant expressions as follows:
A constant expression can be evaluated during translation rather than
runtime, and accordingly may be used in any place that a constant may
be.
Constant expressions can have values of the following type:
o Arithmetic. Any arithmetic operations are allowed. If floating point
is used the precision of the calculations should be at least the same
as in the run time environment.
o A null pointer constant.
o The address of an object with global scope. Optionally an integer
offset can be added to the address.
Since constant expressions are calculated during compilation, even
inefficient algorithms are useful since the execution time is not
affected.
For instance Hallvard B Furuseth proposed (in [1]) a set of clever
macros to calculate the logarithm base 2 of a number during compilation:
/*
* 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) \
Clever isn't it?
So much clever that I have been unable to understand how they work. I
just tested this with the following program:
#include <math.h>
#include <stdio.h>
int main(void)
{
printf("LOG2_32BIT(35986)=%ld\n",LOG2_32BIT(35986));
printf("log2(35986.0)=%g\n",log2(35986.0));
}
OUTPUT:
LOG2_32BIT(35986)=15
log2(35986.0)=15.1351
What is also interesting is that lcc-win receives from the preprocessor
the result of the macro expansion. Here is it, for your amusement
(Of course that is a single huge line that I had to cut in several
places to make it fit the text:
#line 17 "tlog2.c"
int main(void)
{
printf("LOG2_32BIT(35986)=%ld\n",(16*((35986)>65535L) +
(8*(((35986)*1L >>16*((35986)>65535L))>255) +(8 - 90/
(((((35986)*1L >>16*((35986)>65535L)) >>8*(((35986)*
1L >>16*((35986)>65535L))>255))/4+14)|1) - 2/((((35986)*
1L >>16*((35986)>65535L)) >>8*(((35986)*1L >>16*((35986)
The compiler calculates all those operations during compilation, and
outputs the 15, that is stuffed into a register as an argument for the
printf call. Instead of calling an expensive floating point library
function you get the result with no run time penalty.
---
[1] In a message to the comp.lang.c discussion group posted on June 28th
2006, 4:37 pm.
You can find the original message in:
https://groups.google.com/group/comp.lang.c/msg/706324f25e4a60b0?hl=en\&
about constant expressions.
Comments welcome.
--------------------------------------------------------------
Constant expressions
-------------------
The standard defines constant expressions as follows:
A constant expression can be evaluated during translation rather than
runtime, and accordingly may be used in any place that a constant may
be.
Constant expressions can have values of the following type:
o Arithmetic. Any arithmetic operations are allowed. If floating point
is used the precision of the calculations should be at least the same
as in the run time environment.
o A null pointer constant.
o The address of an object with global scope. Optionally an integer
offset can be added to the address.
Since constant expressions are calculated during compilation, even
inefficient algorithms are useful since the execution time is not
affected.
For instance Hallvard B Furuseth proposed (in [1]) a set of clever
macros to calculate the logarithm base 2 of a number during compilation:
/*
* 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) \
Clever isn't it?
So much clever that I have been unable to understand how they work. I
just tested this with the following program:
#include <math.h>
#include <stdio.h>
int main(void)
{
printf("LOG2_32BIT(35986)=%ld\n",LOG2_32BIT(35986));
printf("log2(35986.0)=%g\n",log2(35986.0));
}
OUTPUT:
LOG2_32BIT(35986)=15
log2(35986.0)=15.1351
What is also interesting is that lcc-win receives from the preprocessor
the result of the macro expansion. Here is it, for your amusement
(Of course that is a single huge line that I had to cut in several
places to make it fit the text:
#line 17 "tlog2.c"
int main(void)
{
printf("LOG2_32BIT(35986)=%ld\n",(16*((35986)>65535L) +
(8*(((35986)*1L >>16*((35986)>65535L))>255) +(8 - 90/
(((((35986)*1L >>16*((35986)>65535L)) >>8*(((35986)*
1L >>16*((35986)>65535L))>255))/4+14)|1) - 2/((((35986)*
1L >>16*((35986)>65535L)) >>8*(((35986)*1L >>16*((35986)
65535L))>255))/2+1)))));
The compiler calculates all those operations during compilation, and
outputs the 15, that is stuffed into a register as an argument for the
printf call. Instead of calling an expensive floating point library
function you get the result with no run time penalty.
---
[1] In a message to the comp.lang.c discussion group posted on June 28th
2006, 4:37 pm.
You can find the original message in:
https://groups.google.com/group/comp.lang.c/msg/706324f25e4a60b0?hl=en\&