cconstexprc23

C23 constexpr int cannot be defined using a constexpr float or double


The following code produces an error with gcc in the latest versions:

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
    constexpr double d = 2.2;
    constexpr int i = d*10;
    printf("%d\n", i);
    return EXIT_SUCCESS;
}
<source>:8:23: error: 'constexpr' integer initializer is not an integer constant expression
    7 |     constexpr int i = d*10;
      |                       ^

Casting the product defining i with (int)(d*10) won't work in gcc, but will work with the latest version of clang. But we can do this with no problem:

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
    constexpr double d = 2.2;
    constexpr double temp = d*10;
    constexpr int i = (int)temp;
    printf("%d\n", i);
    return EXIT_SUCCESS;
}

Here's a Godbolt link.

I understand that constexpr floating point computations have some extra complexity and difficulties, based on rounding choice and the current environment, but it seems strange to allow the later and not the former. In the later, the temp value is usable but has to be placed in memory somewhere unnecessarily.

I wonder if I'm missing anything, or if this is just a limitation of the current C23 implementation.


Update: Thanks to Eric Postpischil for the correction that the latest version of clang does allow the first version as well, with the cast.


Solution

  • The observed behavior conforms to the standard.

    C23 6.7.2 para. 6 says:

    If an object or subobject declared with storage-class specifier constexpr has pointer, integer, or arithmetic type, any explicit initializer value for it shall be null,126) an integer constant expression, or an arithmetic constant expression, respectively. […]

    In this case, the object has integer type, so the initializer must be an integer constant expression. C23 6.6 para. 8 says:

    An integer constant expression116) shall have integer type and shall only have operands that are integer constants, named and compound literal constants of integer type, character constants, sizeof expressions whose results are integer constants, alignof expressions, and floating, named, or compound literal constants of arithmetic type 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 typeof operators, sizeof operator, or alignof operator.

    In the initializer (int)(d*10), the immediate operand of the (int) cast, (d*10) is not any of the above; it is only an arithmetic constant expression.

    Note that the following is OK, but it is a bit unwieldy:

    constexpr double d = 2.2;
    constexpr int i = (int)(constexpr double){ d*10 };
    

    In the above, the immediate operand of the cast is a "compound literal constant of arithmetic type", and so it satisfies the requirement for casting to an integer type to form an integer constant expression.

    Whether or not this should be considered to be a limitation, time will tell. Feel free to ask your national standards body to raise a defect report.