Author Topic: [GCC] can't make a calculation as part of variable setup  (Read 10952 times)

0 Members and 1 Guest are viewing this topic.

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: [GCC] can't make a calculation as part of variable setup
« Reply #25 on: August 10, 2018, 09:28:44 am »
This code actually compiles if you use the C++ compiler ('g++') rather than GCC
Code: [Select]
#include <stdio.h>
const int i = 8;
const int j = 10*i;

int main(int argc, char *argv[]) {
   printf("i = %i, j = %i\n",i,j);
}

GCC gives:
Code: [Select]
a.c:3:22: error: initializer element is not constant

So why does 'C' not work? The way I think about it is....

If the compiler is expected to retrieve values from the program's memory to retrieve a value to initialise a static variable, it won't compile.

It fails because the initialization of 'j' requires the compiler to read the memory that will be allocated to hold i.

So why is this the case?

Imagine you had two code modules. The first one was just
Code: [Select]
/* define_i.c Define storage and set values for i */
const int i = 8;

And to go along with that you had a header file, to tell other modules all about 'i', but not what it's value actually is
Code: [Select]
/* define_i.h Header file */
extern const int i;

You might have legitimate reasons for doing this - you might define all your configuration variables in a file called "config.c", and have a "config.h" that allows other C programs to access the config settings.

So now the main module could be:
Code: [Select]
/* main.c */
#include <stdio.h>
#include "define_i.h"
const int j = 10*i;

int main(int argc, char *argv[]) {
   printf("i = %i, j = %i\n",i,j);
}


When the compiler attempts to processes the "main.c" program into an object file, it knows that there is going to be a constant variable called 'i' somewhere in memory ( thanks to the declaration in "define_i.h"), however the value it has is not known to the compiler at that time.

It is only when you have compiled both C programs and link them together that the value for 'i' gets defined.

So why does it work for C++?

C++ has the concept of constructors that (among other things) are used to initialise static variables. These are called before your main() function is run to handle cases like this. It is most likely also the source of undefined/random behaviors, as there is no clear order in which these get called, so most likely best avoided.
« Last Edit: August 10, 2018, 09:34:01 am by hamster_nz »
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline mikeselectricstuff

  • Super Contributor
  • ***
  • Posts: 13748
  • Country: gb
    • Mike's Electric Stuff
Re: [GCC] can't make a calculation as part of variable setup
« Reply #26 on: August 10, 2018, 09:30:46 am »
Also, re #define vs const:

Using #define (preprocessor macro) for constants is a bad practice because the compiler has no way to check the type of the value.

For numerics like this I don't see a problem, maybe even avoids some unnecessary type conversions.possibly even some run-time calculations if the compiler isn't smart enough to flatten them into a compile-time constant.

Quote
#define is basically string replacement/concatenation before even the compiler sees it. So if someone #defines the constant to something unexpected or invalid, you are going to have a much harder time trying to find the problem
Any method is going to have problems with typos etc.

Errors caused by #defines can sometimes be harder to find, though in the case of XC32 which is what I mostly use, it generally tells you where something was defined so typos etc. can usually be found easily.

I'd argue that for something that is fixed at compile time like this, #define makes more sense than const, as const is semntically treated as a variable, which the compiler may or may not be smart enough to deal with efficiently.

Just wondering if all compilers be smart enough to combine multiple floating point consts into an integer result without invoking any FP code?
e.g.
#define clock 24000000
#define divclock clock*0.25


 


« Last Edit: August 10, 2018, 09:36:58 am by mikeselectricstuff »
Youtube channel:Taking wierd stuff apart. Very apart.
Mike's Electric Stuff: High voltage, vintage electronics etc.
Day Job: Mostly LEDs
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17816
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [GCC] can't make a calculation as part of variable setup
« Reply #27 on: August 10, 2018, 09:32:49 am »

The code is designed so that I can use any ADC resolution I like. By changing the one number all constants will adjust as will the oversampling algorithm and anything else.

Then calculate that value once (it is the same value for all instances where you use it) and use the calculated value multiple times. Doing this at runtime, especially on a microcontroller, is a pretty expensive operation for no reason.

But anyway, you can't write the code the way you want - you need to either switch to C++ and use constexpr (assuming your toolchain is modern enough to handle it) or you will have to precalculate these values/use macros because C doesn't allow non-constant expressions in the initializers (const foo is not a constant expression!)

Yes in theory I can do the calculation once, in reality the time it takes at startup/compile is not an issue, one problem at a time.

So in simple language how do I define a constant that is based on another?
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4039
  • Country: nz
Re: [GCC] can't make a calculation as part of variable setup
« Reply #28 on: August 10, 2018, 09:34:52 am »
By the way, this works fine in C. #define isn't the only answer.

Code: [Select]
#include <inttypes.h>

enum {
    ADC_resolution = 13,
    ADC_factor = (1<<ADC_resolution) / 100,
    speed_0 = 0,                            // Percentage of speed
    speed_0_ADC = speed_0 * ADC_factor,     // Pre-calculated value of ADC
    speed_1 = 50,                           // Percentage of speed
    speed_1_ADC = speed_1 * ADC_factor,     // Pre-calculated value of ADC
    speed_2 = 99,                           // Percentage of speed
    speed_2_ADC = speed_2 * ADC_factor,     // Pre-calculated value of ADC

    good_button_press = 50,
    startup_state = 0,
};

uint8_t button_counts;
uint8_t button_state = startup_state;
uint8_t fan_speed;
uint8_t current_fan_speed_input;
uint8_t analogue_fan_channel_timer;

#include <stdio.h>

int main(){
    printf("speed_0_ADC = %d\n", speed_0_ADC);
    printf("speed_1_ADC = %d\n", speed_1_ADC);
    printf("speed_2_ADC = %d\n", speed_2_ADC);
    return 0;
}
 
The following users thanked this post: hans, andyturk, NorthGuy

Offline Kalvin

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
Re: [GCC] can't make a calculation as part of variable setup
« Reply #29 on: August 10, 2018, 09:35:51 am »
Another solution is to call a specific initialization function in main() only once right after device power-up, which would precompute the "constant" values at runtime. A more standard solution is to go with the #define way. One extreme solution is to use an external program/script generating a C header file with precomputed constant values and include this header file into the project.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: [GCC] can't make a calculation as part of variable setup
« Reply #30 on: August 10, 2018, 09:36:09 am »
So in simple language how do I define a constant that is based on another?

Code: [Select]
#define BITS (10)
const int max_val = (1<<BITS)-1;
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17816
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [GCC] can't make a calculation as part of variable setup
« Reply #31 on: August 10, 2018, 09:37:04 am »
So if I make all of these variables does it solve the problem? RAM is not an issue.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: [GCC] can't make a calculation as part of variable setup
« Reply #32 on: August 10, 2018, 09:43:06 am »
By the way, this works fine in C. #define isn't the only answer.

Code: [Select]
#include <inttypes.h>

enum {
    ADC_resolution = 13,
    ADC_factor = (1<<ADC_resolution) / 100,
    speed_0 = 0,                            // Percentage of speed
    speed_0_ADC = speed_0 * ADC_factor,     // Pre-calculated value of ADC
    speed_1 = 50,                           // Percentage of speed
    speed_1_ADC = speed_1 * ADC_factor,     // Pre-calculated value of ADC
    speed_2 = 99,                           // Percentage of speed
    speed_2_ADC = speed_2 * ADC_factor,     // Pre-calculated value of ADC

    good_button_press = 50,
    startup_state = 0,
};

uint8_t button_counts;
uint8_t button_state = startup_state;
uint8_t fan_speed;
uint8_t current_fan_speed_input;
uint8_t analogue_fan_channel_timer;

#include <stdio.h>

int main(){
    printf("speed_0_ADC = %d\n", speed_0_ADC);
    printf("speed_1_ADC = %d\n", speed_1_ADC);
    printf("speed_2_ADC = %d\n", speed_2_ADC);
    return 0;
}

That is nice, in an ugly way  :D, define the values in enums, then store the enums in variables... does it work for floats or doubles?

Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Online IanB

  • Super Contributor
  • ***
  • Posts: 11891
  • Country: us
Re: [GCC] can't make a calculation as part of variable setup
« Reply #33 on: August 10, 2018, 09:47:42 am »
So if I make all of these variables does it solve the problem? RAM is not an issue.

Well, sure. It's the difference between initialization and assignment.

For example, while this won't work:

Code: [Select]
int n = 3;
int m = 5 * n;

This will work:

Code: [Select]
int n, m;
n = 3;
m = 5 * n;
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17816
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [GCC] can't make a calculation as part of variable setup
« Reply #34 on: August 10, 2018, 09:51:02 am »
sounds like constants in C are still bollocks and the only true constant is a define.

So I need to split the operations up.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: [GCC] can't make a calculation as part of variable setup
« Reply #35 on: August 10, 2018, 09:51:43 am »
This will work:

Code: [Select]
int n, m;
n = 3;
m = 5 * n;

Not for me:
Code: [Select]
$ cat main.c
#include <stdio.h>
int i;
int j;
i = 5;
j = i*10;

int main(int argc, char *argv[]) {
           printf("i = %i, j = %i\n",i,j);
}

Code: [Select]
$ gcc -o main main.c
main.c:4:1: warning: data definition has no type or storage class
 i = 5;
 ^
main.c:4:1: warning: type defaults to ‘int’ in declaration of ‘i’ [-Wimplicit-int]
main.c:5:1: warning: data definition has no type or storage class
 j = i*10;
 ^
main.c:5:1: warning: type defaults to ‘int’ in declaration of ‘j’ [-Wimplicit-int]
main.c:5:5: error: initializer element is not constant
 j = i*10;
     ^
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4039
  • Country: nz
Re: [GCC] can't make a calculation as part of variable setup
« Reply #36 on: August 10, 2018, 09:53:43 am »
That is nice, in an ugly way  :D, define the values in enums, then store the enums in variables... does it work for floats or doubles?

No floating point. Only char, short, int, long, long long and the corresponding unsigned variants.
 

Offline mikeselectricstuff

  • Super Contributor
  • ***
  • Posts: 13748
  • Country: gb
    • Mike's Electric Stuff
Re: [GCC] can't make a calculation as part of variable setup
« Reply #37 on: August 10, 2018, 09:53:50 am »
So if I make all of these variables does it solve the problem? RAM is not an issue.

Well, sure. It's the difference between initialization and assignment.

For example, while this won't work:

Code: [Select]
int n = 3;
int m = 5 * n;

This will work:

Code: [Select]
int n, m;
n = 3;
m = 5 * n;
but would be a PITA as it would need to be declared extern in any module that needs it.
I've yet to see a convincing argument (for this situation) against #defining it all in a header file included by all modules. Just remember to bracket any expressions to avoid operator precedence issues
Also simplifies product variants where different sets of constants are used.

 
Youtube channel:Taking wierd stuff apart. Very apart.
Mike's Electric Stuff: High voltage, vintage electronics etc.
Day Job: Mostly LEDs
 

Offline hans

  • Super Contributor
  • ***
  • Posts: 1641
  • Country: nl
Re: [GCC] can't make a calculation as part of variable setup
« Reply #38 on: August 10, 2018, 09:56:33 am »
By the way, this works fine in C. #define isn't the only answer.

Code: [Select]
#include <inttypes.h>

enum {
    ADC_resolution = 13,
    ADC_factor = (1<<ADC_resolution) / 100,
    speed_0 = 0,                            // Percentage of speed
    speed_0_ADC = speed_0 * ADC_factor,     // Pre-calculated value of ADC
    speed_1 = 50,                           // Percentage of speed
    speed_1_ADC = speed_1 * ADC_factor,     // Pre-calculated value of ADC
    speed_2 = 99,                           // Percentage of speed
    speed_2_ADC = speed_2 * ADC_factor,     // Pre-calculated value of ADC

    good_button_press = 50,
    startup_state = 0,
};

uint8_t button_counts;
uint8_t button_state = startup_state;
uint8_t fan_speed;
uint8_t current_fan_speed_input;
uint8_t analogue_fan_channel_timer;

#include <stdio.h>

int main(){
    printf("speed_0_ADC = %d\n", speed_0_ADC);
    printf("speed_1_ADC = %d\n", speed_1_ADC);
    printf("speed_2_ADC = %d\n", speed_2_ADC);
    return 0;
}

 :clap: :clap: :clap:

I think I've seen the EFM32 libraries being built in this way, although at most they were doing was some bit banging for registers.
But I wasn't a real fan of that style. Sure it works and the compiler does it's job, but it can be confusing.

Nonetheless, sounds like there is a lot of debate how to put in constants in a program. Nice to see all the solutions.

I think generally speaking for C with the given tool limitations, most people hardcode numbers into a program or resort to a macro.
Sure you can do better in C++, but speaking as embedded in a single entity, weare not ready for that yet.

Just wondering if all compilers be smart enough to combine multiple floating point consts into an integer result without invoking any FP code?
e.g.
#define clock 24000000
#define divclock clock*0.25


G++ 4.1.2: thinks the outcome is a double (rightly so) and complains when trying to squeeze it into an integer somewhere.
G++ 4.4.7 already does an implicit conversion back to integers..
GCC 4.1.2: does same as G++ 4.4.7
Not sure what other compilers might do, like CCS, IAR or XC8. :-//
 

Online IanB

  • Super Contributor
  • ***
  • Posts: 11891
  • Country: us
Re: [GCC] can't make a calculation as part of variable setup
« Reply #39 on: August 10, 2018, 10:01:44 am »
Not for me:

Well, true. But of course you didn't put the executable statements in a function block. Some details are left for the reader.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17816
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [GCC] can't make a calculation as part of variable setup
« Reply #40 on: August 10, 2018, 10:14:21 am »
OK I am back to defines, but
#define speed_0_ADC ((speed_0 * (((1 << ADC_resolution) - 1) / 100))U) does not work, do I need to care about the "U" as it is always a positive value?
 

Offline mikeselectricstuff

  • Super Contributor
  • ***
  • Posts: 13748
  • Country: gb
    • Mike's Electric Stuff
Re: [GCC] can't make a calculation as part of variable setup
« Reply #41 on: August 10, 2018, 10:17:26 am »
OK I am back to defines, but
#define speed_0_ADC ((speed_0 * (((1 << ADC_resolution) - 1) / 100))U) does not work, do I need to care about the "U" as it is always a positive value?
If you're using #define, you don't include the type, so no "U"
Youtube channel:Taking wierd stuff apart. Very apart.
Mike's Electric Stuff: High voltage, vintage electronics etc.
Day Job: Mostly LEDs
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17816
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [GCC] can't make a calculation as part of variable setup
« Reply #42 on: August 10, 2018, 10:19:06 am »
#define ADC_1_percent (((1 << ADC_resolution) - 1) / 100)  // raw oversampled ADC value of 1%
#define speed_0 0U                                         // Percentage of speed
#define speed_0_ADC (speed_0 * ADC_1_percent)              // Pre-calculated value of ADC
#define speed_1 50U                                        // Percentage of speed
#define speed_1_ADC (speed_1 * ADC_1_percent)              // Pre-calculated value of ADC
#define speed_2 99U                                        // Percentage of speed
#define speed_2_ADC (speed_2 * ADC_1_percent)              // Pre-calculated value of ADC
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17816
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [GCC] can't make a calculation as part of variable setup
« Reply #43 on: August 10, 2018, 10:20:36 am »

If you're using #define, you don't include the type, so no "U"

But we always have to do: #define F_CPU 8000000UL

discreet values with "U" on the end work, the issue is putting it after the formula
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4039
  • Country: nz
Re: [GCC] can't make a calculation as part of variable setup
« Reply #44 on: August 10, 2018, 10:25:28 am »
OK I am back to defines, but
#define speed_0_ADC ((speed_0 * (((1 << ADC_resolution) - 1) / 100))U) does not work, do I need to care about the "U" as it is always a positive value?

*What* is that U doing there?  |O

You can add a "U" as part of the syntax of an integer: 123U. Just like a "." is part of the syntax of a floating point number.

"U" isn't something you can just throw in at random as some kind of type conversion operation.

There's #define speed_0_ADC ((unsigned)(speed_0 * (((1 << ADC_resolution) - 1) / 100)) for that.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: [GCC] can't make a calculation as part of variable setup
« Reply #45 on: August 10, 2018, 10:30:23 am »
OK I am back to defines, but
#define speed_0_ADC ((speed_0 * (((1 << ADC_resolution) - 1) / 100))U) does not work, do I need to care about the "U" as it is always a positive value?

It really isn't needed in this case, but if it was important, you want to use a 'type cast':

Code: [Select]
#define ADC_RESOLUTION (12)
#define ADC_MAX  ((1 << ADC_RESOLUTION) - 1)
#define ADC_SPEED_PERCENT(x) ((uint_32)( x * ADC_MAX / 100))

const uint_32 speed_0_ADC = ADC_SPEED_PERCENT(0)
const uint_32 speed_1_ADC = ADC_SPEED_PERCENT(50)
const uint_32 speed_2_ADC = ADC_SPEED_PERCENT(99)

You also will want to be careful/mindful about overflows too.
« Last Edit: August 10, 2018, 10:34:27 am by hamster_nz »
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline mikeselectricstuff

  • Super Contributor
  • ***
  • Posts: 13748
  • Country: gb
    • Mike's Electric Stuff
Re: [GCC] can't make a calculation as part of variable setup
« Reply #46 on: August 10, 2018, 10:45:28 am »

If you're using #define, you don't include the type, so no "U"

But we always have to do: #define F_CPU 8000000UL
why? surely if a value is big enough to need a long, the compiler will figure that out in the context where the constant gets used.

Only numeric values can have the type suffixes, so if you need them, only put them after things that are numbers.
so :
#define speed_0  1000UL
#deifine ADC_resolution 13UL
#define speed_0_ADC ((speed_0 * (((1 << ADC_resolution) - 1) / 100UL)))
Youtube channel:Taking wierd stuff apart. Very apart.
Mike's Electric Stuff: High voltage, vintage electronics etc.
Day Job: Mostly LEDs
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: [GCC] can't make a calculation as part of variable setup
« Reply #47 on: August 10, 2018, 10:50:43 am »
But we always have to do: #define F_CPU 8000000UL
why?
Those damn 16-bit C compilers used on small micros...
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline janoc

  • Super Contributor
  • ***
  • Posts: 3785
  • Country: de
Re: [GCC] can't make a calculation as part of variable setup
« Reply #48 on: August 10, 2018, 12:03:15 pm »
Also, re #define vs const:

Using #define (preprocessor macro) for constants is a bad practice because the compiler has no way to check the type of the value.

For numerics like this I don't see a problem, maybe even avoids some unnecessary type conversions.possibly even some run-time calculations if the compiler isn't smart enough to flatten them into a compile-time constant.


Never had a problem with differently sized types on different compilers? Or signed vs unsigned? All of that gets muddied up if you use macros - the compiler will promote the literal to whatever type it needs to perform the operation - which may not be the type the programmer has intended (or assumed). If you use a const instead, the type is explicit and if the conversion would be problematic you at least get a warning about it.

Quote
#define is basically string replacement/concatenation before even the compiler sees it. So if someone #defines the constant to something unexpected or invalid, you are going to have a much harder time trying to find the problem
Any method is going to have problems with typos etc.

Errors caused by #defines can sometimes be harder to find, though in the case of XC32 which is what I mostly use, it generally tells you where something was defined so typos etc. can usually be found easily.

I wasn't referring so much to typos (those tend to be obvious) but things such as libraries (re)defining various things behind your back. Typical cases are the min/max macros, size_t, char being redefined as unsigned char, etc. That can break code in subtle ways you won't notice because the code will likely still compile. Just not behave as expected.
 
I'd argue that for something that is fixed at compile time like this, #define makes more sense than const, as const is semntically treated as a variable, which the compiler may or may not be smart enough to deal with efficiently.

That applies only for C where the compiler (or rather the language standard) is sadly too dumb and prevents many of these compile-time optimizations.

Just wondering if all compilers be smart enough to combine multiple floating point consts into an integer result without invoking any FP code?
e.g.
#define clock 24000000
#define divclock clock*0.25

If you define it like this - i.e. macros, then not. The use of divclock gets literally substituted for clock * 0.25 which is of double type. Not to mention that you should put the macro in parentheses, i.e. (clock * 0.25) otherwise you may get an unexpected surprise if it gets used in an expression with a higher priority operator than a multiplication next to it. The preprocessor works really on the text search & replace level, nothing more. I think the xc32 compiler is GCC-based, so you can see the preprocessing result by running it with the -E switch on your source file. It will stop after the preprocessing stage before compilation and you can see the results of the macro substitutions. You will see that the preprocessor does not do any arithmetics there for you.


OTOH, if you use C++11 or newer, you can do this:

constexpr int clock = 24000000;
constexpr int divclock = clock / 4;

and this will be calculated by the compiler at compile time and optimized out, basically ending as equivalent to writing:
constexpr int divclock = 6000000;

If you are wondering why did I replace the multiplication by 0.25 by division by 4 - multiplication by 0.25 will work too but it will end up promoted to double and then truncated into the int constant expression. That will likely trigger a warning about a possible data loss due to the truncation. Division by 4 is an integral division in this case, so no warning. Another way of avoiding that warning would be by using an explicit cast there.
« Last Edit: August 10, 2018, 12:11:29 pm by janoc »
 

Offline janoc

  • Super Contributor
  • ***
  • Posts: 3785
  • Country: de
Re: [GCC] can't make a calculation as part of variable setup
« Reply #49 on: August 10, 2018, 12:13:38 pm »
But we always have to do: #define F_CPU 8000000UL
why?
Those damn 16-bit C compilers used on small micros...

More like 8 bit compilers - that macro is from 8bit AVRs. The compiler itself doesn't need it but a lot of standard AVR library code relies on this macro being defined with the MCUs clock frequency because many functions need to calculate various delays and dividers.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf