Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Limitations and workarounds to expressions defining size of an array

110 views
Skip to first unread message

Francois Grieu

unread,
Sep 4, 2012, 1:14:10 PM9/4/12
to
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.

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, as required by
http://stats.stackexchange.com/q/35658/8469


A tentative (not tested)

#define EXPECTED 15000

/* define an array of EXPECTED + 12*sqrt(EXPECTED)
pointers to struct foo (or slightly more) */
struct foo * bar[
#if (EXPECTED)<900
#error "unsupported EXPECTED value"
-1
#elif (EXPECTED)<3600
(EXPECTED)+360+((EXPECTED)-896)/5
#elif (EXPECTED)<14400
(EXPECTED)+720+((EXPECTED)-3591)/10
#else
(EXPECTED)+1440ul+((EXPECTED)-14381ul)/20
#endif
];

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.

Can I
- use floating point at all ?
- if yes: use sqrt, log, exp ?
- 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?

I can think of enum as intermediates, and have done that
when computing a CRC at compile time, but they have rather
silly range limitations.


Fran�ois Grieu

James Kuyper

unread,
Sep 4, 2012, 2:08:32 PM9/4/12
to
On 09/04/2012 01:14 PM, Francois Grieu wrote:
> 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.


In C99:
6.7.5.2p1: "... the expression shall have an integer type. If the
expression is a constant expression, it shall have a value greater than
zero."
6.7.5.2p2: "... If an identifier is declared to be an object with static
storage duration, it shall not have a variable length array type."
6.7.5.2p4: "... If the size is an integer constant expression and the
element type has a known constant size, the array type is not a variable
length array type; otherwise, the array type is a variable length array
type."
6.7.5.2p5: "If the size is an expression that is not an integer constant
expression expression: if it occurs in a declaration at function
prototype scope, it is treated as if it were replaced by *; otherwise,
each time it is evaluated it shall have a value greater than zero."

C2011:
Change 6.7.5 => 6.7.6
Add "thread storage duration" as another context where VLAs are prohibited.
Implementation support for VLAs is now optional.

C90:
I don't have a copy of C90 to confirm the exact details of the wording,
but VLAs were added in C99, and were therefore not an option in C90.

> 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, as required by
> http://stats.stackexchange.com/q/35658/8469
>
>
> A tentative (not tested)
>
> #define EXPECTED 15000
>
> /* define an array of EXPECTED + 12*sqrt(EXPECTED)
> pointers to struct foo (or slightly more) */
> struct foo * bar[
> #if (EXPECTED)<900
> #error "unsupported EXPECTED value"
> -1
> #elif (EXPECTED)<3600
> (EXPECTED)+360+((EXPECTED)-896)/5
> #elif (EXPECTED)<14400
> (EXPECTED)+720+((EXPECTED)-3591)/10
> #else
> (EXPECTED)+1440ul+((EXPECTED)-14381ul)/20
> #endif
> ];
>
> I think the above works for any EXPECTED>=900 up to
> some large value like ULONG_MAX-ULONG_MAX/19 and then some,

Correct. All of your expressions have integer type and a constant value,
and except for the first one, have a positive value. Unless I missed
something, they should be acceptable in all versions of the standard.

> 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.
>
> Can I
> - use floating point at all ?
> - if yes: use sqrt, log, exp ?

In C90, "No" to both questions. For arrays with static or thread storage
duration, no. However, in C99, for arrays with automatic storage
duration, this is permitted, so long as the entire expression containing
those sub-expressions has an integer type and a positive value. In
C2011, it depends upon whether or not __STDC_NO_VLA__ is pre-#defined by
the implementation.

> - 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?

Whether or not that's permitted depends upon precisely what you mean by
that phrase - it's not clear from context.

Ben Bacarisse

unread,
Sep 4, 2012, 2:18:29 PM9/4/12
to
I don't think so, not in an integer constant expression.

> - if yes: use sqrt, log, exp ?

You can't use functions calls, even in floating point constant
expressions!

> - 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?

I don't know about that.

> I can think of enum as intermediates, and have done that
> when computing a CRC at compile time, but they have rather
> silly range limitations.

If it helps, I thought you might try this:

#define S0 (EXPECTED < 10000 ? 100 : (EXPECTED < 1000000) ? 1000 : 10000)
#define NR(s) (((s) + EXPECTED/(s))/2)
#define SQRT (NR(NR(NR(NR(S0)))))

It is, obviously, Newton-Raphson iteration (4 steps) and gets very close
estimates. For example, with EXPECTED=2147483647, SQRT=S3=46424 instead
of 46340. Add another branch to the conditional (to get a better start
estimate) or add another iteration and you get an exact answer.

You can replace the conditional expression with a set of #if's, or even
define S1, S2 etc to keep the expression size down though it's only
about 1500 characters as it is.

--
Ben.

Kaz Kylheku

unread,
Sep 4, 2012, 2:41:18 PM9/4/12
to
On 2012-09-04, Francois Grieu <fgr...@gmail.com> wrote:
> Can I
> - use floating point at all ?
> - if yes: use sqrt, log, exp ?

No. Constant expressions cannot contain funtion calls.

I would calculate the size at run-time (possibly caching it for re-use)
and then use malloc to grab the storage.

If I really needed a compile constant which requires a complex computation,
I would do that computation in a build configuration script (written in
some interpreted language which has floating-point support), and have it
spit out an include file.

BartC

unread,
Sep 4, 2012, 3:26:33 PM9/4/12
to
"Francois Grieu" <fgr...@gmail.com> wrote in message
news:504636db$0$1709$426a...@news.free.fr...
> 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.
>
> 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, as required by
> http://stats.stackexchange.com/q/35658/8469
>
>
> A tentative (not tested)
>
> #define EXPECTED 15000
>
> /* define an array of EXPECTED + 12*sqrt(EXPECTED)

Perhaps try:

#define sqrt_EXPECTED 122

#define EXPECTED (sqrt_EXPECTED * sqrt_EXPECTED)

with the square root calculation done with a calculator. (You won't get
exactly EXPECTED=15000 though.)

--
Bartc

BartC

unread,
Sep 4, 2012, 3:22:34 PM9/4/12
to
"Kaz Kylheku" <k...@kylheku.com> wrote in message
news:201209041...@kylheku.com...
> On 2012-09-04, Francois Grieu <fgr...@gmail.com> wrote:
>> Can I
>> - use floating point at all ?
>> - if yes: use sqrt, log, exp ?
>
> No. Constant expressions cannot contain funtion calls.

Yet, an expression such as sqrt(9.0) seems to be reduced to 3.0 on the first
two compilers I tried.

It's not much of a stretch to allow that to be used as an array dimension,
the main stumbling block being that 3.0 is not integral, while adding an
(int) cast makes it look like a runtime expression.

--
Bartc

Eric Sosman

unread,
Sep 4, 2012, 5:07:39 PM9/4/12
to
On 9/4/2012 3:22 PM, BartC wrote:
> "Kaz Kylheku" <k...@kylheku.com> wrote in message
> news:201209041...@kylheku.com...
>> On 2012-09-04, Francois Grieu <fgr...@gmail.com> wrote:
>>> Can I
>>> - use floating point at all ?
>>> - if yes: use sqrt, log, exp ?
>>
>> No. Constant expressions cannot contain funtion calls.
>
> Yet, an expression such as sqrt(9.0) seems to be reduced to 3.0 on the
> first two compilers I tried.

Don't confuse definition with optimization. Also, try your
compilers on

strcspn("BartC", "abcdefghijklmnopqrsBCuvwxyz")

... and let us know if they replace it with `3'.

> It's not much of a stretch to allow that to be used as an array
> dimension, the main stumbling block being that 3.0 is not integral,
> while adding an (int) cast makes it look like a runtime expression.

The presence of an (int) cast does not disqualify an expression
from being an "integer constant expression." The function call,
however, does.

--
Eric Sosman
eso...@ieee-dot-org.invalid
"The speed at which the system fails is usually not important."

Barry Schwarz

unread,
Sep 4, 2012, 5:14:22 PM9/4/12
to
On Tue, 04 Sep 2012 19:14:10 +0200, Francois Grieu <fgr...@gmail.com>
wrote:

>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.

There have been questions posted here about applications that compile
and link cleanly but die during startup. Some of those relate to
arrays of excessive size.

For those systems that store automatic variables on the stack,
wouldn't this be a function of stack size? Wouldn't there be a system
specific way to adjust the stack size (if one exists)?

Wouldn't the limit be different if the array had static duration?
Don't different systems have different limits for the amount of memory
an application can claim (and also system specific methods for
changing that limit)?

--
Remove del for email

James Kuyper

unread,
Sep 4, 2012, 5:25:09 PM9/4/12
to
On 09/04/2012 05:07 PM, Eric Sosman wrote:
> On 9/4/2012 3:22 PM, BartC wrote:
>> "Kaz Kylheku" <k...@kylheku.com> wrote in message
>> news:201209041...@kylheku.com...
>>> On 2012-09-04, Francois Grieu <fgr...@gmail.com> wrote:
>>>> Can I
>>>> - use floating point at all ?
>>>> - if yes: use sqrt, log, exp ?
>>>
>>> No. Constant expressions cannot contain funtion calls.
>>
>> Yet, an expression such as sqrt(9.0) seems to be reduced to 3.0 on the
>> first two compilers I tried.
>
> Don't confuse definition with optimization. Also, try your
> compilers on
>
> strcspn("BartC", "abcdefghijklmnopqrsBCuvwxyz")
>
> ... and let us know if they replace it with `3'.

That expression has a value of 0. Were you thinking of strspn()? My
compiler has no problems with the following:

#include <math.h>
#include <stdio.h>
#include <string.h>

struct foo{
int i;
};
int main(int argc, char *argv[])
{
struct foo *bar[(int)ceil(argc + 12.0*sqrt(argc))];
printf("bar: %zu\n", sizeof bar/ sizeof *bar);

int baz[strspn("BartC", "abcdefghijklmnopqrsBCuvwxyz")];
printf("baz: %zu\n", sizeof baz/ sizeof *baz);
return 0;
}

Which produces the output:

bar: 13
baz: 3

when invoked with no arguments, and

bar: 32
baz: 3

when invoked with four arguments. Does your compiler have any problems
with it?

army1987

unread,
Sep 4, 2012, 5:26:29 PM9/4/12
to
On Tue, 04 Sep 2012 17:07:39 -0400, Eric Sosman wrote:

> Don't confuse definition with optimization. Also, try your
> compilers on
>
> strcspn("BartC", "abcdefghijklmnopqrsBCuvwxyz")
>
> ... and let us know if they replace it with `3'.

You meant strspn?



--
[ T H I S S P A C E I S F O R R E N T ]
Troppo poca cultura ci rende ignoranti, troppa ci rende folli.
-- fathermckenzie di it.cultura.linguistica.italiano
<http://xkcd.com/397/>

army1987

unread,
Sep 4, 2012, 5:31:15 PM9/4/12
to
On Tue, 04 Sep 2012 21:26:29 +0000, army1987 wrote:

> On Tue, 04 Sep 2012 17:07:39 -0400, Eric Sosman wrote:
>
>> Don't confuse definition with optimization. Also, try your
>> compilers on
>>
>> strcspn("BartC", "abcdefghijklmnopqrsBCuvwxyz")
>>
>> ... and let us know if they replace it with `3'.
>
> You meant strspn?

BTW, I'm shocked by the result. It optimizes away the call to strspn but
it actually calls printf with arguments "%d\n" and 3? WTF?

Kaz Kylheku

unread,
Sep 4, 2012, 5:39:01 PM9/4/12
to
On 2012-09-04, BartC <b...@freeuk.com> wrote:
> "Kaz Kylheku" <k...@kylheku.com> wrote in message
> news:201209041...@kylheku.com...
>> On 2012-09-04, Francois Grieu <fgr...@gmail.com> wrote:
>>> Can I
>>> - use floating point at all ?
>>> - if yes: use sqrt, log, exp ?
>>
>> No. Constant expressions cannot contain funtion calls.
>
> Yet, an expression such as sqrt(9.0) seems to be reduced to 3.0 on the first
> two compilers I tried.

That has no bearing on what is constitutes "constant expression".

> It's not much of a stretch to allow that to be used as an array dimension,
> the main stumbling block being that 3.0 is not integral, while adding an
> (int) cast makes it look like a runtime expression.

Why does a floating to integer conversion "look" runtime, but a square
root does not?

James Kuyper

unread,
Sep 4, 2012, 5:41:25 PM9/4/12
to
On 09/04/2012 05:31 PM, army1987 wrote:
> On Tue, 04 Sep 2012 21:26:29 +0000, army1987 wrote:
>
>> On Tue, 04 Sep 2012 17:07:39 -0400, Eric Sosman wrote:
>>
>>> Don't confuse definition with optimization. Also, try your
>>> compilers on
>>>
>>> strcspn("BartC", "abcdefghijklmnopqrsBCuvwxyz")
>>>
>>> ... and let us know if they replace it with `3'.
>>
>> You meant strspn?
>
> BTW, I'm shocked by the result. It optimizes away the call to strspn but
> it actually calls printf with arguments "%d\n" and 3? WTF?

The only printf() calls explicitly mentioned in this thread so far were
in my example program, which used "%zu", not "%d", so you're presumably
referring to some other program. What does the source code look like?

Kaz Kylheku

unread,
Sep 4, 2012, 5:44:14 PM9/4/12
to
On 2012-09-04, army1987 <army...@ask-for-it.invalid> wrote:
> On Tue, 04 Sep 2012 21:26:29 +0000, army1987 wrote:
>
>> On Tue, 04 Sep 2012 17:07:39 -0400, Eric Sosman wrote:
>>
>>> Don't confuse definition with optimization. Also, try your
>>> compilers on
>>>
>>> strcspn("BartC", "abcdefghijklmnopqrsBCuvwxyz")
>>>
>>> ... and let us know if they replace it with `3'.
>>
>> You meant strspn?
>
> BTW, I'm shocked by the result. It optimizes away the call to strspn but
> it actually calls printf with arguments "%d\n" and 3? WTF?

Optimizing printf format strings is harder to do than optimizing calls to
strspn.

To optimize strspn (in the above way), all you have to do is check that the two
arguments are both literal constants, and then call strspn inside the
compiler's execution environment, and substitute the result.

To optimize "%d\n" you have to parse the format string.

People implement whatever is easier. (Which is why we have numerous
concessions all over the C language in the first place.)

BartC

unread,
Sep 4, 2012, 5:58:38 PM9/4/12
to


"Kaz Kylheku" <k...@kylheku.com> wrote in message
news:201209041...@kylheku.com...
> On 2012-09-04, BartC <b...@freeuk.com> wrote:
>> "Kaz Kylheku" <k...@kylheku.com> wrote in message
>> news:201209041...@kylheku.com...
>>> On 2012-09-04, Francois Grieu <fgr...@gmail.com> wrote:
>>>> Can I
>>>> - use floating point at all ?
>>>> - if yes: use sqrt, log, exp ?
>>>
>>> No. Constant expressions cannot contain funtion calls.
>>
>> Yet, an expression such as sqrt(9.0) seems to be reduced to 3.0 on the
>> first
>> two compilers I tried.
>
> That has no bearing on what is constitutes "constant expression".

The sqrt() call is obviously being evaluated here as the well-known
mathematical operation rather than an arbitrary C function. (Unless the
compiler is clever enough to evaluate and reduce the C code inside an actual
sqrt() function somewhere.)

So the compiler could easily include mathematical functions amongst what are
considered constant expressions. That it doesn't do so is presumably the
standard saying it's not allowed (because it's not always practical to do).
That's unfortunate, because it would be handy.

>> It's not much of a stretch to allow that to be used as an array
>> dimension,
>> the main stumbling block being that 3.0 is not integral, while adding an
>> (int) cast makes it look like a runtime expression.
>
> Why does a floating to integer conversion "look" runtime, but a square
> root does not?

From the error messages I got ('non-integer expression' with sqrt(), rather
than 'dynamic expression' with (int)sqrt(), although I can't replicate those
now..)

--
Bartc


Kaz Kylheku

unread,
Sep 4, 2012, 6:12:54 PM9/4/12
to
That's just a consequence of the order in which some checks are applied.

Suppose that some context requires an integral, constant expression.

If you use an expression which is floating-point, and non-constant,
which error message will appear? A complaint that the expression isn't
integral, or that it's non-constant?

If the compiler stops checking an expression after the first constraint
is found, it's going to be whatever is checked first.

Eric Sosman

unread,
Sep 4, 2012, 6:24:06 PM9/4/12
to
On 9/4/2012 5:25 PM, James Kuyper wrote:
> On 09/04/2012 05:07 PM, Eric Sosman wrote:
>> On 9/4/2012 3:22 PM, BartC wrote:
>>> "Kaz Kylheku" <k...@kylheku.com> wrote in message
>>> news:201209041...@kylheku.com...
>>>> On 2012-09-04, Francois Grieu <fgr...@gmail.com> wrote:
>>>>> Can I
>>>>> - use floating point at all ?
>>>>> - if yes: use sqrt, log, exp ?
>>>>
>>>> No. Constant expressions cannot contain funtion calls.
>>>
>>> Yet, an expression such as sqrt(9.0) seems to be reduced to 3.0 on the
>>> first two compilers I tried.
>>
>> Don't confuse definition with optimization. Also, try your
>> compilers on
>>
>> strcspn("BartC", "abcdefghijklmnopqrsBCuvwxyz")
>>
>> ... and let us know if they replace it with `3'.
>
> That expression has a value of 0. Were you thinking of strspn()?

Indeed, I was. Too much 'c', I guess. ;-)

> [...] Does your compiler have any problems
> with it?

BartC reports the compilers he tested implemented `sqrt(9.0)'
as `3.0', implying that the actual function call was not performed
at run-time. (He presumably used extra-linguistic means to find
this out.) He went on to suggest that this somehow put `sqrt(9.0)'
on an equal footing with a "constant expression." My example was
intended to show another function whose value is knowable before
run-time, hence similar to `sqrt(9.0)', but most likely not similarly
optimized. That is, I was trying to show the fallacy of reasoning
from observed behavior backward to the Standard, instead of from the
Standard forward to required behavior.

What I showed instead was carelessness, sigh.

Lowell Gilbert

unread,
Sep 4, 2012, 6:55:04 PM9/4/12
to
"BartC" <b...@freeuk.com> writes:

> "Kaz Kylheku" <k...@kylheku.com> wrote in message
> news:201209041...@kylheku.com...
>> On 2012-09-04, BartC <b...@freeuk.com> wrote:
>>> "Kaz Kylheku" <k...@kylheku.com> wrote in message
>>> news:201209041...@kylheku.com...
>>>> On 2012-09-04, Francois Grieu <fgr...@gmail.com> wrote:
>>>>> Can I
>>>>> - use floating point at all ?
>>>>> - if yes: use sqrt, log, exp ?
>>>>
>>>> No. Constant expressions cannot contain funtion calls.
>>>
>>> Yet, an expression such as sqrt(9.0) seems to be reduced to 3.0 on
>>> the first
>>> two compilers I tried.
>>
>> That has no bearing on what is constitutes "constant expression".
>
> The sqrt() call is obviously being evaluated here as the well-known
> mathematical operation rather than an arbitrary C function. (Unless
> the compiler is clever enough to evaluate and reduce the C code inside
> an actual sqrt() function somewhere.)

A number of compilers are clever enough to do that, but not necessarily
on a lot of different functions.

> So the compiler could easily include mathematical functions amongst
> what are considered constant expressions. That it doesn't do so is
> presumably the standard saying it's not allowed (because it's not
> always practical to do). That's unfortunate, because it would be
> handy.

Preprocessor manipulations are a fairly rough tool for a number of other
reasons as well. Accordingly, I try to avoid doing anything overly
complicated with them. One alternative is to code the various values and
build a test program to check that they have the correct relationships;
this works more easily when the host platform is also the target
platform, but I've done it in embedded systems also. Another approach is
to generate the values as a separate step before compilation.

>>> It's not much of a stretch to allow that to be used as an array
>>> dimension,
>>> the main stumbling block being that 3.0 is not integral, while adding an
>>> (int) cast makes it look like a runtime expression.
>>
>> Why does a floating to integer conversion "look" runtime, but a square
>> root does not?
>
> From the error messages I got ('non-integer expression' with sqrt(),
> rather than 'dynamic expression' with (int)sqrt(), although I can't
> replicate those now..)

That may not be interesting; if several diagnostics apply, it won't
necessarily give you all of them. The specific messages can depend on
the parsing state at the time it notices an error. It's nice when a
compiler gives you all appropriate messages, but sometimes it's not
practical.

--
Lowell Gilbert, embedded/networking software engineer
http://be-well.ilk.org/~lowell/

Keith Thompson

unread,
Sep 4, 2012, 7:51:36 PM9/4/12
to
"BartC" <b...@freeuk.com> writes:
> "Kaz Kylheku" <k...@kylheku.com> wrote in message
> news:201209041...@kylheku.com...
>> On 2012-09-04, Francois Grieu <fgr...@gmail.com> wrote:
>>> Can I
>>> - use floating point at all ?
>>> - if yes: use sqrt, log, exp ?
>>
>> No. Constant expressions cannot contain funtion calls.
>
> Yet, an expression such as sqrt(9.0) seems to be reduced to 3.0 on the
> first two compilers I tried.

Yes, I'm sure that's a common optimization.

But the phrase "constant expression" is defined by the standard in
terms of what it can contain; it doesn't merely mean "an expression
that a sufficiently clever compiler can evaluate at compile time".

> It's not much of a stretch to allow that to be used as an array
> dimension, the main stumbling block being that 3.0 is not integral,
> while adding an (int) cast makes it look like a runtime expression.

But where do you draw the line between constant and non-constant
expressions? The authors of the standard already decided exactly
where to draw that line. Moving it in the direction of treating
more expressions as constant risks imposing requirements that some
compilers may not be able to meet.

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Will write code for food.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Keith Thompson

unread,
Sep 4, 2012, 8:29:06 PM9/4/12
to
Eric Sosman <eso...@ieee-dot-org.invalid> writes:
[...]
> That is, I was trying to show the fallacy of reasoning
> from observed behavior backward to the Standard, instead of from the
> Standard forward to required behavior.
>
> What I showed instead was carelessness, sigh.

What you showed *in addition* was carelessness.

Eric Sosman

unread,
Sep 4, 2012, 8:58:41 PM9/4/12
to
On 9/4/2012 7:51 PM, Keith Thompson wrote:
> "BartC" <b...@freeuk.com> writes:
>> "Kaz Kylheku" <k...@kylheku.com> wrote in message
>> news:201209041...@kylheku.com...
>>> On 2012-09-04, Francois Grieu <fgr...@gmail.com> wrote:
>>>> Can I
>>>> - use floating point at all ?
>>>> - if yes: use sqrt, log, exp ?
>>>
>>> No. Constant expressions cannot contain funtion calls.
>>
>> Yet, an expression such as sqrt(9.0) seems to be reduced to 3.0 on the
>> first two compilers I tried.
>
> Yes, I'm sure that's a common optimization.
>
> But the phrase "constant expression" is defined by the standard in
> terms of what it can contain; it doesn't merely mean "an expression
> that a sufficiently clever compiler can evaluate at compile time".
>
>> It's not much of a stretch to allow that to be used as an array
>> dimension, the main stumbling block being that 3.0 is not integral,
>> while adding an (int) cast makes it look like a runtime expression.
>
> But where do you draw the line between constant and non-constant
> expressions? The authors of the standard already decided exactly
> where to draw that line. Moving it in the direction of treating
> more expressions as constant risks imposing requirements that some
> compilers may not be able to meet.

The Standard draws a line and says "All C compilers accept
everything on this side of the line as constants." But the line
is permeable: "An implementation may accept other forms of constant
expressions" (6.6p10). The upshot:

1) A C compiler must accept `static int x[2+1];', because
`2+1' is on the hither side of the line,

2) A C compiler may reject `static int x[strlen("abc")];'
because `strlen("abc")' is on the thither side, but

3) A C compiler may accept `static int x[strlen("abc")];'
under the "other forms" allowance, and need not even
issue a diagnostic.

Cases (2) and (3) illustrate why "It works with my compiler"
does not define the language.

Francois Grieu

unread,
Sep 5, 2012, 3:09:23 AM9/5/12
to
On 04/09/2012 20:08, James Kuyper wrote:
> On 09/04/2012 01:14 PM, Francois Grieu wrote:
>> 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.
>
>
> In C99:
> 6.7.5.2p1: "... the expression shall have an integer type. If the
> expression is a constant expression, it shall have a value greater than
> zero."
> 6.7.5.2p2: "... If an identifier is declared to be an object with static
> storage duration, it shall not have a variable length array type."
> 6.7.5.2p4: "... If the size is an integer constant expression and the
> element type has a known constant size, the array type is not a variable
> length array type; otherwise, the array type is a variable length array
> type."
> 6.7.5.2p5: "If the size is an expression that is not an integer constant
> expression expression: if it occurs in a declaration at function
> prototype scope, it is treated as if it were replaced by *; otherwise,
> each time it is evaluated it shall have a value greater than zero."

Yes; also relevant is 6.6p6
An integer constant expression (.. used to specify .. the size of an
array ..) shall have integer type and shall only have operands
that are integer constants, enumeration constants, character
constants, sizeof expressions whose results are integer constants,
and floating constants that are the immediate operands of casts.
Cast operators in an integer constant expression shall only
convert arithmetic types to integer types, except as part of an
operand to the sizeof operator.

I read that as C99 NOT allowing (except in automatic context, as a
VLA) even basic use of floating point as in

struct foo * bar[(unsigned)(1000*2.718)];


>> I hoped at least to use intermediate values (other than
>> macro identifiers) to clarify things and remain below
>> the "4095 characters in a logical source line" limit

> Whether or not that's permitted depends upon precisely what you mean
> by that phrase - it's not clear from context.

I was hoping for (not tested)

// select temporary type
#include <limits.h>
#ifdef ULLONG_MAX
#define UTMP static const unsigned long long
#else
#define UTMP static const unsigned long
#endif

#define EXPECTED 15000
UTMP kExpected = EXPECTED;
UTMP kRoot2 = 12*12*Expected;
// we want ceil(sqrt(12*12*Expected)), use Newton/Rapson
UTMP kExTmp0 = (uwl)3<<(kRoot2>>30 > 0 ? 15 :
kRoot2>>24 > 0 ? 12 :
kRoot2>>18 > 0 ? 9 :
kRoot2>>12 > 0 ? 6 : 3);
UTMP kExTmp1 = (kExTmp0+kRoot2/kExTmp0)/2;
UTMP kExTmp2 = (kExTmp1+kRoot2/kExTmp1)/2;
UTMP kExTmp3 = (kExTmp2+kRoot2/kExTmp2)/2;
UTMP kExTmp4 = (kExTmp3+kRoot2/kExTmp3)/2;
UTMP kExTmp5 = (kExTmp4+kRoot2/kExTmp4)/2;
UTMP kExTmp6 = (kExTmp5+kRoot2/kExTmp5)/2;
UTMP kExTmp7 = (kExTmp6+kRoot2/kExTmp6)/2;
UTMP kExTmp8 = (kExTmp7+kRoot2/kExTmp7)/2;
UTMP kExTmp9 = (kExTmp8+kRoot2/kExTmp8)/2;
UTMP kExTmpA = (kExTmp9+kRoot2/kExTmp9)/2;
UTMP kExTmpB = (kExTmpA+kRoot2/kExTmpA)/2;
UTMP kExTmpC = (kExTmpB+kRoot2/kExTmpB)/2;
UTMP kRoot = kExTmpC+(kExTmpB>kExTmpC?1:0);

struct foo * bar[kExpected+kRoot];

Is that valid in C11?

Francois Grieu

Francois Grieu

unread,
Sep 5, 2012, 3:56:36 AM9/5/12
to
On 04/09/2012 20:08, James Kuyper wrote:
> On 09/04/2012 01:14 PM, Francois Grieu wrote:
>> 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.
>
>
> In C99:
> 6.7.5.2p1: "... the expression shall have an integer type. If the
> expression is a constant expression, it shall have a value greater than
> zero."
> 6.7.5.2p2: "... If an identifier is declared to be an object with static
> storage duration, it shall not have a variable length array type."
> 6.7.5.2p4: "... If the size is an integer constant expression and the
> element type has a known constant size, the array type is not a variable
> length array type; otherwise, the array type is a variable length array
> type."
> 6.7.5.2p5: "If the size is an expression that is not an integer constant
> expression expression: if it occurs in a declaration at function
> prototype scope, it is treated as if it were replaced by *; otherwise,
> each time it is evaluated it shall have a value greater than zero."

Yes; also relevant is 6.6p6
An integer constant expression (.. used to specify .. the size of an
array ..) shall have integer type and shall only have operands
that are integer constants, enumeration constants, character
constants, sizeof expressions whose results are integer constants,
and floating constants that are the immediate operands of casts.
Cast operators in an integer constant expression shall only
convert arithmetic types to integer types, except as part of an
operand to the sizeof operator.

I read that as C99 NOT allowing (except in automatic context, as a
VLA) even basic use of floating point as in

struct foo * bar[(unsigned)(1000*2.718)];


>> I hoped at least to use intermediate values (other than
>> macro identifiers) to clarify things and remain below
>> the "4095 characters in a logical source line" limit

> Whether or not that's permitted depends upon precisely what you mean
> by that phrase - it's not clear from context.

I was hoping for
// select temporary type
#include <limits.h>
#ifdef ULLONG_MAX
typedef unsigned long long uw;
#else
typedef unsigned long uw;
#endif
#define UTMP static const uw

#define EXPECTED 15000
UTMP kExpected = EXPECTED;
UTMP kRoot2 = 12*12*kExpected;
// we want ceil(sqrt(12*12*kExpected)), use Newton/Rapson
UTMP kExTmp0 = (uw)3<<(kRoot2>>30 > 0 ? 15 :
kRoot2>>24 > 0 ? 12 :
kRoot2>>18 > 0 ? 9 :
kRoot2>>12 > 0 ? 6 : 3);
UTMP kExTmp1 = (kExTmp0+kRoot2/kExTmp0)/2;
UTMP kExTmp2 = (kExTmp1+kRoot2/kExTmp1)/2;
UTMP kExTmp3 = (kExTmp2+kRoot2/kExTmp2)/2;
UTMP kExTmp4 = (kExTmp3+kRoot2/kExTmp3)/2;
UTMP kExTmp5 = (kExTmp4+kRoot2/kExTmp4)/2;
UTMP kExTmp6 = (kExTmp5+kRoot2/kExTmp5)/2;
UTMP kExTmp7 = (kExTmp6+kRoot2/kExTmp6)/2;
UTMP kExTmp8 = (kExTmp7+kRoot2/kExTmp7)/2;
UTMP kExTmp9 = (kExTmp8+kRoot2/kExTmp8)/2;
UTMP kExTmpA = (kExTmp9+kRoot2/kExTmp9)/2;
UTMP kExTmpB = (kExTmpA+kRoot2/kExTmpA)/2;
UTMP kExTmpC = (kExTmpB+kRoot2/kExTmpB)/2;
UTMP kRoot = kExTmpC+(kExTmpB>kExTmpC?1:0);

struct foo * bar[kExpected+kRoot];


This is not valid C99 (the definition of kRoot2 is invalid);
is that valid in C11? <ot>Note: it seem valid in C++</ot>.

Francois Grieu

Francois Grieu

unread,
Sep 5, 2012, 4:04:49 AM9/5/12
to
On 05/09/2012 02:58, Eric Sosman wrote:
> The Standard draws a line and says "All C compilers accept
> everything on this side of the line as constants." But the line
> is permeable: "An implementation may accept other forms of constant
> expressions" (6.6p10). The upshot:
>
> 1) A C compiler must accept `static int x[2+1];', because
> `2+1' is on the hither side of the line,
>
> 2) A C compiler may reject `static int x[strlen("abc")];'
> because `strlen("abc")' is on the thither side, but
>
> 3) A C compiler may accept `static int x[strlen("abc")];'
> under the "other forms" allowance, and need not even
> issue a diagnostic.
>
> Cases (2) and (3) illustrate why "It works with my compiler"
> does not define the language.

Well said.

Unless I err, we have a portable idiom equivalent to that:

static int x[sizeof("abc")-1];


Francois Grieu

James Kuyper

unread,
Sep 5, 2012, 7:17:04 AM9/5/12
to
On 09/05/2012 03:09 AM, Francois Grieu wrote:
> On 04/09/2012 20:08, James Kuyper wrote:
>> On 09/04/2012 01:14 PM, Francois Grieu wrote:
...
> Yes; also relevant is 6.6p6
> An integer constant expression (.. used to specify .. the size of an
> array ..) shall have integer type and shall only have operands
> that are integer constants, enumeration constants, character
> constants, sizeof expressions whose results are integer constants,
> and floating constants that are the immediate operands of casts.
> Cast operators in an integer constant expression shall only
> convert arithmetic types to integer types, except as part of an
> operand to the sizeof operator.
>
> I read that as C99 NOT allowing (except in automatic context, as a
> VLA) even basic use of floating point as in
>
> struct foo * bar[(unsigned)(1000*2.718)];

That is correct. Which is why, if you want to do this, it will have to
be an array with automatic storage duration. Does it need to have static
storage duration?

...
> I was hoping for (not tested)
>
> // select temporary type
> #include <limits.h>
> #ifdef ULLONG_MAX
> #define UTMP static const unsigned long long
> #else
> #define UTMP static const unsigned long
> #endif

I'd recommend using a typedef rather than #define for that purpose. If
you do use a typedef, you should also change the name, since
fully-capitalized names are, by convention, used mainly for macros.

> #define EXPECTED 15000
> UTMP kExpected = EXPECTED;
> UTMP kRoot2 = 12*12*Expected;
> // we want ceil(sqrt(12*12*Expected)), use Newton/Rapson
> UTMP kExTmp0 = (uwl)3<<(kRoot2>>30 > 0 ? 15 :
> kRoot2>>24 > 0 ? 12 :
> kRoot2>>18 > 0 ? 9 :
> kRoot2>>12 > 0 ? 6 : 3);
> UTMP kExTmp1 = (kExTmp0+kRoot2/kExTmp0)/2;
> UTMP kExTmp2 = (kExTmp1+kRoot2/kExTmp1)/2;
> UTMP kExTmp3 = (kExTmp2+kRoot2/kExTmp2)/2;
> UTMP kExTmp4 = (kExTmp3+kRoot2/kExTmp3)/2;
> UTMP kExTmp5 = (kExTmp4+kRoot2/kExTmp4)/2;
> UTMP kExTmp6 = (kExTmp5+kRoot2/kExTmp5)/2;
> UTMP kExTmp7 = (kExTmp6+kRoot2/kExTmp6)/2;
> UTMP kExTmp8 = (kExTmp7+kRoot2/kExTmp7)/2;
> UTMP kExTmp9 = (kExTmp8+kRoot2/kExTmp8)/2;
> UTMP kExTmpA = (kExTmp9+kRoot2/kExTmp9)/2;
> UTMP kExTmpB = (kExTmpA+kRoot2/kExTmpA)/2;
> UTMP kExTmpC = (kExTmpB+kRoot2/kExTmpB)/2;
> UTMP kRoot = kExTmpC+(kExTmpB>kExTmpC?1:0);
>
> struct foo * bar[kExpected+kRoot];
>
> Is that valid in C11?

I don't see any problems. Did you have any particular reason for
doubting that it was valid? If so, you might be right - I'm far less
reliable when I say "I see no problems" than when I say "I see a problem".
--
James Kuyper

James Kuyper

unread,
Sep 5, 2012, 7:33:45 AM9/5/12
to
On 09/05/2012 03:56 AM, Francois Grieu wrote:
...
Ah - I see the point you're raising now. Sorry for missing it in your
previous message - I didn't notice your use of 'static' in the
#definition of UTMP. Initializers for an object of static storage
duration must be constant expressions. In C++, a const variable
initialized with a constant expression can itself be used as a constant
expression, but in C it is not allowed. You'd have to re-write this as a
single constant expression, and you'll have to be careful to avoid
running into line length limitations if you do so. I haven't checked to
be sure, but you might have settle for fewer iterations.

If you remove the 'static' and define these inside a function, there
should be no problem except with the definition of bar itself, which
requires VLA support. Of course, if you can use a VLA, you wouldn't need
to use such a round-about method to do this, you could simply calculate
the sqrt() directly.
--
James Kuyper

army1987

unread,
Sep 5, 2012, 4:45:25 PM9/5/12
to
#include <stdio.h>
int main(void)
{
printf("%d\n", (int)strspn("BartC", "abcdefghijklmnopqrsBCuvwxyz"));
return 0;

James Kuyper

unread,
Sep 5, 2012, 5:23:04 PM9/5/12
to
On 09/05/2012 04:45 PM, army1987 wrote:
> On Tue, 04 Sep 2012 17:41:25 -0400, James Kuyper wrote:
>
>> On 09/04/2012 05:31 PM, army1987 wrote:
...
>>> BTW, I'm shocked by the result. It optimizes away the call to strspn
>>> but it actually calls printf with arguments "%d\n" and 3? WTF?
>>
>> The only printf() calls explicitly mentioned in this thread so far were
>> in my example program, which used "%zu", not "%d", so you're presumably
>> referring to some other program. What does the source code look like?
>
> #include <stdio.h>
> int main(void)
> {
> printf("%d\n", (int)strspn("BartC", "abcdefghijklmnopqrsBCuvwxyz"));
> return 0;
> }

OK, that's roughly what I expected, but I wanted to be sure. In
particular, your "WTF?" left me wondering whether you thought the
optimization was invalid. I'm mildly impressed by that optimization, but
not nearly as much as you were, apparently.

army1987

unread,
Sep 5, 2012, 5:30:31 PM9/5/12
to
On Wed, 05 Sep 2012 17:23:04 -0400, James Kuyper wrote:

> In
> particular, your "WTF?" left me wondering whether you thought the
> optimization was invalid.

I don't. But 1) I was surprised that strspn() was optimized away, and 2)
I seemed to remember seeing a compiler optimizing away printf() some time
ago, so I expected that if a compiler would do the former it would do the
latter too.

Francois Grieu

unread,
Sep 5, 2012, 5:39:56 PM9/5/12
to
On 05/09/2012 13:33, James Kuyper wrote:
> (..) Initializers for an object of static storage
> duration must be constant expressions. In C++, a const variable
> initialized with a constant expression can itself be used as a constant
> expression, but in C it is not allowed. You'd have to re-write this as a
> single constant expression, and you'll have to be careful to avoid
> running into line length limitations if you do so. I haven't checked to
> be sure, but you might have settle for fewer iterations.

I think that I found a conforming workaround: use enums instead
of constants. There is the limitation that enums can only be
trusted to hold 15 bits, but I can workaround that too using
the following ZS and ZG macros.


// select temporary type uw
#include <limits.h>
#ifdef ULLONG_MAX
typedef unsigned long long uw;
#else
typedef unsigned long uw;
#endif

// Utility macro to set symbol s to value v (60-bit capacity)
#define ZS(s,v) enum{e0##s=(v)&32767,e1##s=(v)>>15&32767,\
e2##s=(v)>>15>>15&32767,e3##s=(v)>>15>>15>>15&32767}

// Utility macro to get value of symbol s (60-bit capacity)
#define ZG(s) (((((uw)e3##s<<15|e2##s)<<15|e1##s))<<15|e0##s)

// Set of macros to compute floor(sqrt(x)) for x>0.
// Find a raw approximation of sqrt(x)
#define SQRT0(x) ((uw)8<<(3*((x)<512?0:(x)>>15<1?1:(x)>>15<64?2:\
(x)>>15>>9<8?3:(x)>>15>>15<8?4:(x)>>15>>15>>9<1?5:\
(x)>>15>>15>>15<1?6:(x)>>15>>15>>15<64?7:(x)>>15>>15>>15>>9<8?8:9)))
// One Newton-Raphson refinement step
#define SQRT1(x,a) (((x)/(a)+(a))/2)
// Four Newton-Raphson refinement steps
#define SQRT4(x,a) (SQRT1(x,SQRT1(x,SQRT1(x,SQRT1(x,a)))))
// Final tweak to get exact value
#define SQRTF(x,a) (SQRT1(x,a)-(SQRT1(x,a)>(a)?1:0))

#define EXPECTED 400000000

// Now we want:
// struct foo * bar[EXPECTED+ceil(12*sqrt(EXPECTED)];
// that is
// struct foo * bar[EXPECTED+1+floor(sqrt(12*12*EXPECTED-1)];
#define KVALUE (12*12*(uw)(EXPECTED)-1)
ZS(kExTmp0, SQRT0(KVALUE)); // first approximation
ZS(kExTmp1, SQRT4(KVALUE,ZG(kExTmp0))); // improve..
ZS(kExTmp2, SQRT4(KVALUE,ZG(kExTmp1))); // improve..
ZS(kExTmp3, SQRT4(KVALUE,ZG(kExTmp2))); // improve..
#define KROOT SQRTF(KVALUE,ZG(kExTmp3)) // finalize
struct foo * bar[EXPECTED+1+KROOT];


This works and (as far as I can tell) is conformant, but many
will understandably frown on it as hairy. Especially if we push
it further and implement floating-point-as-enum.


Questions: In C11, what liberties do we have?
In particular,
- can we define the size of a static array (non-VLA)
using a static const long ?
- can we use a static const long freely in an
expression that defines another static const long ?
- can we use floating point arithmetic in expressions defining
a static const long ?
- can we use exp() and log() in such expressions ?

Francois Grieu

Ben Bacarisse

unread,
Sep 5, 2012, 6:04:35 PM9/5/12
to
army1987 <army...@ask-for-it.invalid> writes:

> On Wed, 05 Sep 2012 17:23:04 -0400, James Kuyper wrote:
>
>> In
>> particular, your "WTF?" left me wondering whether you thought the
>> optimization was invalid.
>
> I don't. But 1) I was surprised that strspn() was optimized away, and 2)
> I seemed to remember seeing a compiler optimizing away printf() some time
> ago, so I expected that if a compiler would do the former it would do the
> latter too.

gcc will replace some printf calls that use %s with calls to simpler
functions like puts, but replacing a call that uses a %d format is at
least two steps: convert the constant integer to a string and then
replace the printf call. I suspect the conversion puts too great a
distance between what's written and the improved result.

An implementation could choose to replace printf calls that is %d with
other functions designed to printf numbers directly, but that would mean
introducing special functions for this relatively rare optimisation.
Had C already had print_int and so on I imagine it might have been
considered (is that hedged enough!?).

--
Ben.

Eric Sosman

unread,
Sep 5, 2012, 6:11:18 PM9/5/12
to
On 9/5/2012 5:39 PM, Francois Grieu wrote:
> [...]
> Questions: In C11, what liberties do we have?
> In particular,
> - can we define the size of a static array (non-VLA)
> using a static const long ?

Sure:

static const long scl = 42;
static int array[sizeof scl];

But for the question I think you're really asking, the
answer is No. First read 6.7.6.2p4 to learn that an array
with a dimension that is not an integer constant expression
is a VLA. Then read 6.6p6 to find out how an ICE is formed;
note that `static const long' does not match any of the allowed
operands. Hence an expression using the value of an SCL is
not an ICE, and an array declared with such an expression as
its dimension is a VLA.

> - can we use a static const long freely in an
> expression that defines another static const long ?

No. 6.6p7-9.

> - can we use floating point arithmetic in expressions defining
> a static const long ?

Yes. 6.6p8.

> - can we use exp() and log() in such expressions ?

No. 6.6p6-9.

6.6p10 offers a loophole: "An implementation may accept
other forms of constant expressions," so for any particular
compiler some of the Noes may turn to Yesses. But you wrote
"freely," which I'm interpreting as "works on all C11 compilers,"
and with that understanding the Noes have it.

Eric Sosman

unread,
Sep 5, 2012, 6:17:39 PM9/5/12
to
On 9/5/2012 5:30 PM, army1987 wrote:
> On Wed, 05 Sep 2012 17:23:04 -0400, James Kuyper wrote:
>
>> In
>> particular, your "WTF?" left me wondering whether you thought the
>> optimization was invalid.
>
> I don't. But 1) I was surprised that strspn() was optimized away, and 2)
> I seemed to remember seeing a compiler optimizing away printf() some time
> ago, so I expected that if a compiler would do the former it would do the
> latter too.

Pre-computation of strspn() surprised me, too. But compilers
have been handling certain special cases of printf() for a long
time. More than fifteen years ago, a colleague told me of writing
the transformation

printf("%s\n", ptr); /* as written */
to
puts(ptr); /* as compiled */

... with the usual justification: "One of the SPEC benchmarks used
the printf() a lot, and we got a speed increase by eliminating the
format interpretation." <Checks> A fairly elderly gcc does this,
even at the default optimization level.

James Kuyper

unread,
Sep 5, 2012, 6:29:03 PM9/5/12
to
On 09/05/2012 05:30 PM, army1987 wrote:
> On Wed, 05 Sep 2012 17:23:04 -0400, James Kuyper wrote:
>
>> In
>> particular, your "WTF?" left me wondering whether you thought the
>> optimization was invalid.
>
> I don't. But 1) I was surprised that strspn() was optimized away, and 2)
> I seemed to remember seeing a compiler optimizing away printf() some time
> ago, so I expected that if a compiler would do the former it would do the
> latter too.

What this particular format string causes printf() to do is relatively
simple. The whole thing could have been optimized all the way down to
fputs("3", stdout). However, in general printf() is far more complicated
than strspn(), which might explain why the strspn() call got evaluated
at compile time, while the printf() call did not.

In particular, printf()'s behavior depends upon the current locale,
while strspn()'s does not. In this simple program, it's easy to confirm
that there's no possibility of setlocale() being called. However, the
need to check for that possibility might have discouraged any attempt to
pre-evaluate the printf() call.

Tim Rentsch

unread,
Sep 7, 2012, 7:41:02 AM9/7/12
to
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.)

Tim Rentsch

unread,
Sep 7, 2012, 7:47:00 AM9/7/12
to
The principle is right, the specific example is wrong.
Expressions that contain a function call and that must
be constant expressions are constraint violations and
must have a diagnostic issued, regardless of whether
an implementation chooses to regard them as constant
expressions.

Tim Rentsch

unread,
Sep 7, 2012, 7:53:10 AM9/7/12
to
Keith Thompson <ks...@mib.org> writes:

> "BartC" <b...@freeuk.com> writes:
>> "Kaz Kylheku" <k...@kylheku.com> wrote in message
>> news:201209041...@kylheku.com...
>>> On 2012-09-04, Francois Grieu <fgr...@gmail.com> wrote:
>>>> Can I
>>>> - use floating point at all ?
>>>> - if yes: use sqrt, log, exp ?
>>>
>>> No. Constant expressions cannot contain funtion calls.
>>
>> Yet, an expression such as sqrt(9.0) seems to be reduced to 3.0 on the
>> first two compilers I tried. [snip]
>
> But where do you draw the line between constant and non-constant
> expressions? The authors of the standard already decided exactly
> where to draw that line. [snip]

More specifically, they have drawn two lines, one representing
a minimum set of what are constant expressions, and one
representing which constructs are always out of bounds for
constant expressions -- kind of a lower bound and an upper
bound. And function calls are over the "not allowed" line.

BartC

unread,
Sep 7, 2012, 8:07:02 AM9/7/12
to
"Tim Rentsch" <t...@alumni.caltech.edu> wrote in message
news:kfnhara...@x-alumni2.alumni.caltech.edu...
> Keith Thompson <ks...@mib.org> writes:

>> But where do you draw the line between constant and non-constant
>> expressions? The authors of the standard already decided exactly
>> where to draw that line. [snip]
>
> More specifically, they have drawn two lines, one representing
> a minimum set of what are constant expressions, and one
> representing which constructs are always out of bounds for
> constant expressions -- kind of a lower bound and an upper
> bound. And function calls are over the "not allowed" line.

Perhaps it's about time then that the built-in mathematical functions were
classed as operators, not as arbitrary functions.

Compilers already do that for optimising, but aren't allowed to do that when
the code depends on it (expressions defining array bounds for example,
because it would break on another compiler.)

There are some numeric implications (eg. an array bound could vary slightly
between compilers and machines) but there already are many issues with
floating point anyway. And in examples like the OP's, it wouldn't really
matter.

--
Bartc

army1987

unread,
Sep 7, 2012, 9:32:24 AM9/7/12
to
On Tue, 04 Sep 2012 19:14:10 +0200, Francois Grieu wrote:

> 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, as required by

I would do this:

#define EXPECTED 15000 /* When changing this, please also change
ARRAY_SIZE accordingly. */
#define ARRAY_SIZE 16470 /* ceil(EXPECTED + 12*sqrt(EXPECTED)) */

Keith Thompson

unread,
Sep 7, 2012, 5:04:02 PM9/7/12
to
Tim Rentsch <t...@alumni.caltech.edu> writes:
[...]
> Expressions that contain a function call and that must
> be constant expressions are constraint violations and
> must have a diagnostic issued, regardless of whether
> an implementation chooses to regard them as constant
> expressions.

I'm not convinced that's correct.

N1370 6.6p10 says

An implementation may accept other forms of constant expressions.

To take one example of a context requiring a constant expression,
6.8.4.2p3 says:

The expression of each case label shall be an integer constant
expression and no two of the case constant expressions in the
same switch statement shall have the same value after conversion.

If an implementation accepts `foo()` as a constant expression, then
using `foo()` in a case label doens't violate that constraint, because
it's a constant expression *for that implementation*.

This is different than the general permission to provide extensions
given by 4p6:

A conforming implementation may have extensions (including
additional library functions), provided they do not alter the
behavior of any strictly conforming program.

which does (I think!) require a diagnostic for any code that would
violate a constraint in the absence of the extension.

Keith Thompson

unread,
Sep 7, 2012, 5:08:50 PM9/7/12
to
Sorry, but I don't think that's a good idea. Consider, for example, a
cross compiler where the host and target systems have differing
floating-point precision. (Compilers need to handle this for
floating-point constant, but not for any more complicated expression.)

I think you're understating the problems caused by floating-point
rounding errors. It could be implementation-defined (or unspecified)
a given switch statement is legal, depending on whether the
expressions in two case labels evaluate to the same integer value.

And how often do you really need to use a floating-point function in a
constant expression? I don't think it's worth the additional complexity.

Casey Carter

unread,
Sep 7, 2012, 5:40:23 PM9/7/12
to
On 2012-09-07 16:04, Keith Thompson wrote:
> This is different than the general permission to provide extensions
> given by 4p6:
>
> A conforming implementation may have extensions (including
> additional library functions), provided they do not alter the
> behavior of any strictly conforming program.
>
> which does (I think!) require a diagnostic for any code that would
> violate a constraint in the absence of the extension.

It's somewhat orthogonal to the issue at hand, but I'm in the mood for
pointless semantic arguments today. I suggest that "any code that would
violate a constraint in the absence of the extension" is necessarily NOT
within the sphere of "any strictly conforming program."

Tim Rentsch

unread,
Sep 7, 2012, 10:41:35 PM9/7/12
to
"BartC" <b...@freeuk.com> writes:

> "Tim Rentsch" <t...@alumni.caltech.edu> wrote in message
> news:kfnhara...@x-alumni2.alumni.caltech.edu...
>> Keith Thompson <ks...@mib.org> writes:
>
>>> But where do you draw the line between constant and non-constant
>>> expressions? The authors of the standard already decided exactly
>>> where to draw that line. [snip]
>>
>> More specifically, they have drawn two lines, one representing
>> a minimum set of what are constant expressions, and one
>> representing which constructs are always out of bounds for
>> constant expressions -- kind of a lower bound and an upper
>> bound. And function calls are over the "not allowed" line.
>
> Perhaps it's about time then that the built-in mathematical
> functions were classed as operators, not as arbitrary
> functions. [snip elaboration]

Way more trouble than it's worth. If you want to allow
function calls in constant expressions, it's easier
just to move them from the Constraints part of section 6.6
to the Semantics part of the same section.

Tim Rentsch

unread,
Sep 7, 2012, 10:54:48 PM9/7/12
to
Keith Thompson <ks...@mib.org> writes:

> Tim Rentsch <t...@alumni.caltech.edu> writes:
> [...]
>> Expressions that contain a function call and that must
>> be constant expressions are constraint violations and
>> must have a diagnostic issued, regardless of whether
>> an implementation chooses to regard them as constant
>> expressions.
>
> I'm not convinced that's correct. [snip elaboration]

This issue was addressed in Defect Report # 261, which reads in
part:

* If the syntax or context only permits a constant
expression, the constraints of 6.6#3 and 6.6#4 shall apply.

* Otherwise [ie, if the constraints above are not violated],
if the expression meets the requirements of 6.6 (including
any form accepted in accordance with 6.6#10), it is a
constant expression.

* Otherwise it is not a constant expression.

The phrase in []'s is my comment, but I'm confident it's
correct because otherwise the second bullet item makes
no sense. In any case here is the link if you want to
read the whole thing (be warned! some of the examples
flow off the right side of the page):

http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_261.htm

Keith Thompson

unread,
Sep 8, 2012, 5:15:27 PM9/8/12
to
"In summary, provided the above points are taken account of, the
Committee does not believe the Standard is ambiguous nor that it is
necessary to modify it to make this clearer."

Clive D.W. Feather wasn't entirely sure what the Standard meant, but
it's not necessary to make it any clearer.

Riiiight.

lawrenc...@siemens.com

unread,
Sep 17, 2012, 12:09:08 PM9/17/12
to
Keith Thompson <ks...@mib.org> wrote:
>
> "In summary, provided the above points are taken account of, the
> Committee does not believe the Standard is ambiguous nor that it is
> necessary to modify it to make this clearer."
>
> Clive D.W. Feather wasn't entirely sure what the Standard meant, but
> it's not necessary to make it any clearer.
>
> Riiiight.

To be fair, my friend Clive has a penchant for going out of his way to
not be entirely sure what the Standard means. It's a trait that's as
valueable as it is annoying, so we always give his input serious
consideration and we end up agreeing with him more often than not. But
not always.
--
Larry Jones

What a waste to be going to school on a morning like this. -- Calvin
0 new messages