Francois Grieu <
fgr...@gmail.com> writes:
> I'm trying to figure out the exact limitations that (possibly:
> various versions of ISO/IEC 9899) C puts to expressions defining
> the size of a plain array at compile time, and how to workaround
> that. [.. rearranging ..] Can I
> - use floating point at all ?
Not portably.
> - if yes: use sqrt, log, exp ?
No.
> - or at least, use intermediate values (other than
> macro identifiers) to clarify things and remain below
> the "4095 characters in a logical source line" limit?
Note that "logical source lines" occur in translation phase 2,
whereas macro expansion is done in phase 4 (and is done in terms
of tokens, not characters). The 4095 character limit affects
macro definitions, not macro expansions.
> I can think of enum as intermediates, [snip] but they have
> rather silly range limitations.
Yes, generally expressions using macro expansion are better
than using enum constants.
> An application would be, given EXPECTED, to define at compile
> time an array with EXPECTED + 12*sqrt(EXPECTED) elements, or
> some even more complex formula, [snip example]
>
> I think the above works for any EXPECTED>=900 up to
> some large value like ULONG_MAX-ULONG_MAX/19 and then some,
> and errs on the safe side, with at worse 5% wasted memory;
> but this is
> - unclear at best;
> - painful to extend to lower bounds or less wasted memory;
> - hard to generalize to more complex formula involving
> for example log or exp.
If you need to do something like this, generally a good way of
approaching it is to divide the input space up into ranges and
find a good integer approximation in each range. There can be
interplay between the ranges and the approximations chosen. For
example, using ceil( 12 * sqrt( x ) ) as the desired target (and
must never be less than that, or less than one), the target
function could be approximated using
#define SQ12(n) SQ12_((n))
#define SQ12_(n) ( \
n < 2 ? 12*n + (n==0) : \
n < 3 ? 17 : \
n < 4 ? 21 : \
n < 16 ? SQx(12*n/8,144*n) : \
n < 1055 ? SQx(12*n/13,144*n) : \
n < 26866 ? SQx(12*n/80,144*n) : \
n < 361166 ? SQx(12*n/331,144*n) : \
n < 3321166 ? SQx(12*n/1093,144*n): \
n < 22000000 ? SQx(12*n/2992,144*n) : \
n < 160000000 ? 12 * SQx(n/6543,n) : \
n < 1200000000 ? 12 * SQx(n/12500,n) : \
12 * SQx(n/45000,n) \
)
#define SQx( k, n ) SQx_((k),(n))
#define SQx_( k, n ) (1+SQ3_(k,n))
#define SQ3_( k, n ) SQ2_(SQ1_(k,n),n)
#define SQ2_( k, n ) SQ1_(SQ1_(k,n),n)
#define SQ1_( k, n ) ((k+n/k)/2)
#define EXPECTED 6999999
int HERE = SQ12( EXPECTED );
Obviously the ranges and specific constants were chosen by
experimentation. Naturally there is a tradeoff between the
complexity involved and the accuracy desired (and also what
input domain needs to be covered). The particular definition
above works over all 32 bit inputs, and has good accuracy (off
by at most one up to n = 20000000, and within a factor of
1.005 above that). Of course it is possible to find simpler
formulations or more accurate ones; the point is to pick an
approximation that suits the needs of the problem to be solved
-- dividing into ranges and using various approximations in
the various ranges is general and flexible, understandable,
and also fairly cheap in terms of expanded token count.
Incidentally, the sample code above generates an expansion
(the line with HERE in it) of about 3000 characters.
(Ben's suggestion is also good. For anyone interested I think
it's worth looking at the similarities and differences of the
two approaches.)