This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler/TMS320F28377D: Macro token concatenation is broken for C99 hex floats

Part Number: TMS320F28377D

Tool/software: TI C/C++ Compiler

The following code does not compile with CGT 17.6.0.STS (and probably other versions, I haven't checked).

#define CONCATENATE( a, b ) a ## b
float a = +0x1.c801aap-2;
float b = CONCATENATE( +, 0x1.c801aap-2 );
float c = CONCATENATE( +0x1., c801aap-2 );
float d = CONCATENATE( +0x1.c801aa, p-2 );

CCS output is:

**** Build of configuration Debug for project test_hexfloat_macro ****

"C:\\ti\\ccsv6\\utils\\bin\\gmake" -k all
'Building file: ../main.c'
'Invoking: C2000 Compiler'
"C:/ti/ccsv6/tools/compiler/ti-cgt-c2000_17.6.0.STS/bin/cl2000" -v28 -ml -mt --cla_support=cla1 --float_support=fpu32 --tmu_support=tmu0 --vcu_support=vcu2 --include_path="C:/ti/ccsv6/tools/compiler/ti-cgt-c2000_17.6.0.STS/include" -g --diag_warning=225 --diag_wrap=off --display_error_number --gen_preprocessor_listing --preproc_with_compile --preproc_dependency="main.d" "../main.c"

>> Compilation failure
subdir_rules.mk:7: recipe for target 'main.obj' failed
"../main.c", line 3: warning #1934-D: concatenation with "0x1.c801aap-2" in macro "CONCATENATE" does not create a valid token
"../main.c", line 4: error #168: invalid floating constant
"../main.c", line 5: error #168: invalid floating constant
2 errors detected in the compilation of "../main.c".
gmake: *** [main.obj] Error 1
gmake: Target 'all' not remade because of errors.

**** Build Finished ****

Yet the preprocessor listing suggests the output of the preprocessor is identical in all instances:

L 1 "../main.c"
N#define CONCATENATE( a, b ) a ## b
Nfloat a = +0x1.c801aap-2;
W "../main.c" 3 11 concatenation with "0x1.c801aap-2" in macro "CONCATENATE" does not create a valid token
Nfloat b = CONCATENATE( +, 0x1.c801aap-2 );
Xfloat b = +0x1.c801aap-2;
E "../main.c" 4 11 invalid floating constant
Nfloat c = CONCATENATE( +0x1., c801aap-2 );
Xfloat c = +0x1.c801aap-2;
E "../main.c" 5 11 invalid floating constant
Nfloat d = CONCATENATE( +0x1.c801aa, p-2 );
Xfloat d = +0x1.c801aap-2;

And the preprocessed source (*.pp file) implies this is also the case:

float a = +0x1.c801aap-2;
float b = +0x1.c801aap-2;
float c = +0x1.c801aap-2;
float d = +0x1.c801aap-2;

What is going on here? Am I violating the C preprocessor standard?

  • I don't understand all the nuances yet.  But I can tell you that I have not found any C compiler which accepts that source code.  

    Here is what it looks like when I try with GCC version 5.4.0 ...

    $ gcc -c file.c
    file.c:3:24: error: pasting "+" and "0x1.c801aap-2" does not give a valid preprocessing token
     float b = CONCATENATE( +, 0x1.c801aap-2 );
                            ^
    file.c:1:29: note: in definition of macro ‘CONCATENATE’
     #define CONCATENATE( a, b ) a ## b
                                 ^
    file.c:4:25: error: exponent has no digits
     float c = CONCATENATE( +0x1., c801aap-2 );
                             ^
    file.c:1:29: note: in definition of macro ‘CONCATENATE’
     #define CONCATENATE( a, b ) a ## b
                                 ^
    file.c:5:25: error: exponent has no digits
     float d = CONCATENATE( +0x1.c801aa, p-2 );
                             ^
    file.c:1:29: note: in definition of macro ‘CONCATENATE’
     #define CONCATENATE( a, b ) a ## b
                                 ^

    Thanks and regards,

    -George

  • Try compiling it in C99 mode. Use option --c99
  • Looking carefully at the C99 standard, a hexadecimal-floating-constant starts with 0x; the + or - in front of the value is not part of the floating constant token. Thus, you may not attempt to add a leading + or - with the ## token-pasting operator. If you want an expression with positive value, just put the + there without using the ## operator, like so:

    float b = + /* mind the gap! */ 0x1.c801aap-2;

    As to the other lines, it is required that both operands of ## themselves are valid preprocessing tokens. The kind of preprocessing token you need here is a pp-number (See C99 section 6.4.8 "Preprocessing numbers") . While 0x1. is not a valid hexadecimal-float-constant, it is a valid pp-number. However, c801aap-2 is not because it starts with a letter... In other words, you can't splice hex numbers with token pasting if the first digit in the second argument isn't a decimal digit.

    GCC agrees with the TI compiler that this use of ## is not valid.

    What problem are you trying to solve with token pasting? Perhaps we can find a legal way to write it?

  • We are attempting to write portable hex floats, because rounding of decimal floats is implementation defined. We are targeting TI CGT and MSDEV. TI CGT takes C99 hex floats. MSDEV doesn't. We were looking for a way to construct hex floats for TI CGT using sign, mantissa and exponent.

    In the end I think we will go the way we have to in MSDEV and use UINT32s, reinterpreting as floats as we go along.

  • I know you want a compile time constant, but maybe you can use a union and a Macro to set the bits as you need them.

    This is not portable and may not work with this compiler.

    You might also just use a separate header and plain #DEFINE's.
  • Well yes, rounding is implementation-defined, but the rounding mode is probably the same between TI (round to even) and MS.

    I can't quickly picture how you're going to use sign, hex mantissa, and hex exponent to construct the float in MS if it doesn't accept hex float, but you can do it like this for TI without token pasting. The parser will trivially fold the multiplication.

    float x = + 0x1.c801aap+0 * 0x1p-2
  • Keith Barkley said:
    maybe you can use a union and a Macro to set the bits as you need them.

    Before doing that, try __u32_bits_as_f32(0x3ee400d5) with the TI compiler.  This doesn't go through memory, so it's cheaper.