CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Math in define (I believe a basic error with float)

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
ChicoDaRave



Joined: 21 Nov 2014
Posts: 10

View user's profile Send private message

Math in define (I believe a basic error with float)
PostPosted: Thu Mar 05, 2015 4:39 pm     Reply with quote

Hello.

I'm trying to do some simple math in a define for calculating ADC values in a resistor divisor.
Looks like this:
Code:
#define R1 220
#define R2  47
#define ADCVref 5
#define ADCMaxVal 255
#define VTB(x) (int8)((ADCMaxVal/ADCVref)*((R2/(R1+R2))*x))

But the resulting ASM is CLRF 24
I believe it is calculating as integer, so something * zero is zero.

I simplified the math to #define VTB(x) (int8)((255/5)*((47/267)*x))
But same result.
I got a result when added ".0" in all values or to 47 or 267, but 255 and 5 not, which makes sense.

When working with values I can "easily" add ".0" to values, but if I work with defines or variables, how can I do this?
Can I keep R2 define value as just 47 or I really need to put 47.0?
There is another way to force it to calculate as float and later convert to integer?

I have other questions also.
1. There is some way to print something in the output console?
2. There is an way to change C/ASM values from Hex to decimal?

Thank you very much.


Last edited by ChicoDaRave on Thu Mar 05, 2015 5:11 pm; edited 1 time in total
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Thu Mar 05, 2015 4:48 pm     Reply with quote

what happens if you just
code it as a normal function?

what FLOAT ?
how are you trying to use the define in your program?
what var types are your other items in this thing?

define x !!!

absent casting var types - this is a disastrous bit of mess.
'C is NOT BASIC........

Code:


int8 VTB( int8 x)  {

 return(ADCMaxVal/ADCVref)*((R2/(R1+R2))*x);
}


what you defined makes no sense to me.

And the compiler is surely just as confused Very Happy Very Happy Very Happy Very Happy Very Happy
ChicoDaRave



Joined: 21 Nov 2014
Posts: 10

View user's profile Send private message

PostPosted: Thu Mar 05, 2015 4:58 pm     Reply with quote

asmboy, this math is just to make easier and tidier to make the code.
Will use like this:
If (ADC <= VTB(21))...

VTB(21) will mean 21v in the resistor divider input.

Got it?
Sorry, I dont know how to explain it better than this :(
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Mar 05, 2015 5:04 pm     Reply with quote

Quote:
When working with values I can "easily" add ".0" to values, but if I work
with defines or variables, how can I do this?


An alternative to appending .0 on the end, is to use casting. Example:
Code:
(float)R2

or
Code:
(float)ADCMaxVal

etc.
alan



Joined: 12 Nov 2012
Posts: 357
Location: South Africa

View user's profile Send private message

PostPosted: Fri Mar 06, 2015 12:05 am     Reply with quote

The problem are that 47/267 = 0. The way I do that, is to reshuffle the maths so that I first do all the multiplication and then divide as the compiler wants to work in ints if you do not force floats.

Regards
Ttelmah



Joined: 11 Mar 2010
Posts: 19358

View user's profile Send private message

PostPosted: Fri Mar 06, 2015 2:00 am     Reply with quote

Alan has said it all.

The key to understand is that float is not being used anywhere. None of the items are float, so float will not be used. The types in a define do not always behave exactly as they do in the language itself, but in general they behave very similarly. So if you start at the innermost section:

R2/(R1+R2)

With all the values coded as integer, this is always going to use integer maths, so is always going to give 0.....

A cast, or adding .0 to R2, forces float to be used throughout.

As a comment, doing your define in the C in Unix also codes as 0. Not a CCS problem, but a failure to understand C.
RF_Developer



Joined: 07 Feb 2011
Posts: 839

View user's profile Send private message

PostPosted: Fri Mar 06, 2015 2:54 am     Reply with quote

Ttelmah wrote:
Alan has said it all.

The key to understand is that float is not being used anywhere. None of the items are float, so float will not be used. The types in a define do not always behave exactly as they do in the language itself, but in general they behave very similarly.


I agree, but I'll add this: the types should behave exactly as in the language, because no maths at all are done in defines, or the preprocessing step. Defines, and C pre-processing in general is just text substitution and string manipulation. Anything that looks like maths in the defines is done, after all the relevant textual substitution, by the compiler itself. It therefore MUST follow the rules of C and be syntactically correct C for it to work. All the normal C typing rules apply.

Note, some, or all of the "maths in defines" can be done at compile time, and just because there is a divide sign, it doesn't mean a divide actually gets done at run time by your PIC. The compiler does what is called "constant collapse" and pre-computes, using the normal rules and syntax of C, the results of expressions formed from constants and replaces them with an equivalent single constant. Its often useful to be aware of this, and to ensure your defines do generate constants, even if the maths in them looks quite complicated.

One word of warning: constant collapse can only happen with simple functions, such as the basic four functions. Maths which are defined as actual C functions, such as sin(), log() etc, in CCS C at least (not necessarily, but most likely in all Cs), even when they have constant inputs, have to be evaluated at run time. The programmer has to pre-compute these and put the resulting number in the define.

I use the same sort of thing for my ADC scaling. I generally accept that the final scaling will require a float operation. I arrange my constants to generate something that I can multiply, as obviously floating division takes much longer. Occasionally I can use a much simpler integer scaling, but for completeness, and to help anyone maintaining my code in the future to understand where the number came from, I tend to still use the same process, but just use the much simpler integer constant.

Personally I don't like to use function macros in situations like this. You can be at the mercy of what the user decides to put in as a parameter, and that can mess with the syntax of the result, giving unexpected results. Also, adding in a (probably) variable breaks the concept of having simple constants, and possibly forces the PIC to do more work at runtime than you are expecting. I prefer for my defined stuff, in this context, to always evaluate to constants that can be predictably collapsed by the complier, and only the operations the PIC does are those that the user uses in their code (as opposed to my defines/macros). So, no hidden, unexpected maths operations.

Here's some of my "maths in defines" from a current (undebugged at the time of posting) project of mine:

Code:

// Analogue calibration and conversion constants.

#define ADC_VREF                 4.096f         // Reference voltage                         
#define ADC_COUNTS               4096           // 12 bits unipolar
#define ADC_VOLTS_PER_BIT        (ADC_VREF / ADC_COUNTS)

#define GATE_SENSE_R             220
#define GATE_F_R                 47e3                                // Required for op-amp circuits. Just use xxxx_SENSE_GAIN for fixed gain amps.
#define GATE_IN_R                47e3                                // One or both of these should be float to ensure GATE_SENSE_GAIN is float.
#define GATE_SENSE_GAIN          (GATE_F_R / GATE_IN_R)              // This is float if the above condition has bees met.
#define GATE_EFF_SENSE_R         (GATE_SENSE_R * GATE_SENSE_GAIN)    // Actual sense resistance times buffer gain - also float
#define GATE_UA_PER_BIT          (1e6 * ADC_VOLTS_PER_BIT/GATE_EFF_SENSE_R)   // 1e6 gives uA and forces result to be float.

...
// This is how its used. This requires just one floating multiplication at runtime, despite all the multiplies and divides in the defines.
// This is because GATE_UA_PER_BIT evaluates in the compiler, i.e. "collapses", to just a simple, single float constant.
Gates_Current_uA = (int16)(ADC_Value * GATE_UA_PER_BIT);


Note how I've used "f" to force a constant to be treated as a float, and used exponent notation on others (resistor values - I prefer to think about 47K than 47000 Ohms). As you already know, using a decimal point also forces the compiler to treat a number as float. Also I've got lots of explanatory comments and done it step by step to make it clear what's going on.


Last edited by RF_Developer on Fri Mar 06, 2015 3:51 am; edited 1 time in total
Ttelmah



Joined: 11 Mar 2010
Posts: 19358

View user's profile Send private message

PostPosted: Fri Mar 06, 2015 3:14 am     Reply with quote

Yes.

There are fractional differences in CCS, but 'fractional'.
The one I know happens relates to 'signed'. If you put a signed value into a #define, it always seems to behave as a signed int16. However in the actual language, for PIC12/16/18, it will if the value is small enough, code as a signed int8.
It's a very small difference, but is why I can only say 'in general they behave very similarly'.
I suspect it is actually because the core code for this part, is common to the different compiler versions, with their different integer sizes....
RF_Developer



Joined: 07 Feb 2011
Posts: 839

View user's profile Send private message

PostPosted: Fri Mar 06, 2015 3:44 am     Reply with quote

Ttelmah wrote:

There are fractional differences in CCS, but 'fractional'.
The one I know happens relates to 'signed'. If you put a signed value into a #define, it always seems to behave as a signed int16.


Yes, as the pre-processor step is rolled into the compiler, there's room for "cheating" and playing fast and loose with syntax. With an old-fashioned separate pre-processor that's not possible as the compilation, and hence the syntax, can have nothing to do with the text substitutions as they are done by completely separate applications. CCS C shouldn't do that sort of thing, but in some cases does. It does that with quite a few things - naughty CCS, but I still much prefer it to the alternatives for PICs.
ChicoDaRave



Joined: 21 Nov 2014
Posts: 10

View user's profile Send private message

PostPosted: Sat Mar 07, 2015 3:19 am     Reply with quote

Thanks for all answers.

I was 99,999% sure the problem was just lack of my knowledge about C and CCS.
Sorry for my lack of knowledge, If I had a lot knowledge for sure I would not bother you all with such newbie question.

My idea is to really use the compiler to do the math, not the microcontroller.
I want just to make the code easier for my head understand during the coding and later on updating/upgrading as I can quickly and smartly change the resistor values for example.
I want all math to be done as float in compiler, but in microcontroller code EVERYTHING will be integer.
I believe my only error was the lack of some float value in the math, right?

Casting looks a good solution.
Can I do this?
#define VTB(x) (int8)((ADCMaxVal/ADCVref)*(((float)R2/(R1+R2))*x))

Thank you again.
Ttelmah



Joined: 11 Mar 2010
Posts: 19358

View user's profile Send private message

PostPosted: Sat Mar 07, 2015 4:02 am     Reply with quote

Almost.

Do this instead:
Code:


#define VTB(x) (int8)((ADCMaxVal/ADCVref)*((R2/(R1+(float)R2))*x))


You always work out from the innermost thing. So if the operation R1+R2, is 'float', then the division will use float etc..

Now casting R2 where you show, may work, but the problem is that if the compiler elects to use int8 arithmetic for R1+R2, this value will overflow. It would in the real chip.
If you find the very innermost operation, and promote this to the higher type, and then everything else will use this higher type. Smile

As a comment, 255, is not right.....
The PIC's ADC, though it returns an 8/10/12bit value (so apparently 255 'fence panels' for 8bits, versus 256 'fence posts'), is scaled internally so it reaches the full value a bit _below_ the full voltage, so the correct divisor is 256 (this was an idea first developed by Texas some years ago, to simplify the calculation). have a look at the published application notes about the transfer function. There are some very early PIC's where this is not true, but the current ones are all scaled this way.

On the real chip, if you were wanting to save processing as much as possible, you'd actually do what Alan said. Perform the multiplications first, using integer maths, and only perform one division at the end. So:

Code:

#define VTB(x) (int8)((x*ADCMaxVal*(int32)R2)/((R1+(int16)R2)*ADCVref))

This then does the work in integer to give an integer result.
So (for example, using your values), for x=10, gives:
(10*255*47)/(267*5) = 119850/1335 = 89
ChicoDaRave



Joined: 21 Nov 2014
Posts: 10

View user's profile Send private message

PostPosted: Sat Mar 07, 2015 1:11 pm     Reply with quote

Ttelmah, I thank you for your answer, but my knowledge in C and in english is poor, so could not understand some things you wrote.

"You always work out from the innermost thing" Sorry, what you mean with this?

As I said, my knowledge is poor, I'm just trying to learn.

As I said, all this math will be done by the compiler, none in the real chip, I have no intend of doing this math in chip.
If compiler choses int8 for R1+R2 is because R1 and R2 are int8 isnt it?

I understand if I would do the math in the chip the ideal would be simplify the math at maximum and avoid divisions, but the case here is just make the code tidier and easier for a newbie like me code and understand the code later, also, learn something about C and CCS.

I will need some time to understand your calculation but I'm sure it is same calculation but simplified.

About the ADC, you already told me about that but I dont know how to workaround it, It is just put 256 instead of 255?

Thank you again.
Ttelmah



Joined: 11 Mar 2010
Posts: 19358

View user's profile Send private message

PostPosted: Sat Mar 07, 2015 1:16 pm     Reply with quote

The top formula shows where to put the cast for the #define.

The second one shows you how to organise the function if you were calculating in the chip. This function does the maths doing integer arithmetic, by multiplying first.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group