Simple Math

Go To Last Post
18 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello All,

 

could please someone explain me why this very simple math is not working.

 

math

 

The result is alway zero.

 

Is there somewhere a tutorial for this subject available?

 

Thank you in advance

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Usually that means you're doing fixed point arithmetic division when you thought you might be getting a floating point intermediate result.  C has a rule about using "int" for intermediate calculations, unless it's explicitly inappropriate.

ie

   volts = 5 * ADCreading/1024;

will always result in zero.  It SHOULD be:

   volts = Vref * (float)ADCreading)/1024.0;

(or something similar.)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Life is much simpler when you copy-paste text from your editor or Serial window.

 

I have to type in your lines by hand.

ADC_input_voltage = ADC_conversion_value * (5/1024);
//evaluates as
ADC_input_voltage = ADC_conversion_value * (0);
//
// rearange your parentheses:
ADC_input_voltage = (ADC_conversion_value * 5)/1024);
//
// alternatively use f-p constants
ADC_input_voltage = ADC_conversion_value * (5.0 /1024);
//evaluates as
ADC_input_voltage = ADC_conversion_value * (0.0048828125);

The rules for C expression evaluation order are quite straightforward.    But your head will hurt less if you play safe with sensible placement of parentheses.

 

Oh.   f-p is very useful.   Do not worry about processing time.   It will be insignificant compared to the ADC conversion time.    And most importantly it makes your code easier to maintain (and understand)

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
(or something similar.)
westfw wrote:
C has a rule about using "int" for intermediate calculations, unless it's explicitly inappropriate.

Not exactly, in this case--but kind of close.

 

[side note:  the optimizer may consider the whole exercise pointless as the result isn't being used.  so maybe assign to a 'volatile'...]

 

OP told the compiler to do an integer operation, and "forced" it with the parentheses:

(5 / 1024) will always be integer 0. 

Then it will be used as float 0 to do the multiplication.

 

Generally, as ADC result is a small integer, I'll do all my calculations in e.g. millivolts and seldom [almost never] have floats in my AVR8 apps.  But if you want that, do (5./1024) or (5./1024.)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

using integer math, (5/1024) = zero, so any ADCreading * zero = zero!

 

To avoid using floating point math, calculate voltage in milivolts.

  miliVolts = (ADCreading * 488)/100;

 

Jim

 

 

FF = PI > S.E.T

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you smiley.

 

I was asking me since yesterday why this don't work.....

 

Now it works finne

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

ki0bk wrote:
To avoid using floating point math, calculate voltage in milivolts. miliVolts = (ADCreading * 488)/100;

Well, Jim, now you have introduced the overflow possibility with the default "int" as mentioned earlier.  As a ADC reading can be ~1000, then your intermediat result could be ~500000 and does not fit into 16 bits (or 15 bits plus sign).

 

So either the intermediate operations must be cast to a wider type, or a conversion factor used that will not cause overflow.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Well, Jim, now you have introduced the overflow possibility with the default "int" as mentioned earlier.

 

You are correct: I should have written: miliVolts = (ADCreading * 488UL) / 100;

Just in case some one is searches for the topic....

Thanks,

 

Jim

 

 

FF = PI > S.E.T

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

ki0bk wrote:
I should have written:...

In almost all cases, one can find a small integer ratio that is good enough; usually far beyond the possible three decimals in even  a perfect ADC.  For example, in this case 39/8 and 44/9 are good to about 1 part in 1000 -- IME "good enough" for true microcontroller work.  (note the need to force the operations to unsigned 16 bits as the multiplier is >32)

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

theusch wrote:
Well, Jim, now you have introduced the overflow possibility with the default "int" as mentioned earlier.

 

You are correct: I should have written: miliVolts = (ADCreading * 488UL) / 100;

Just in case some one is searches for the topic....

You made a good point about bumping up to millivolts for the ADC scaling, and yes, you need to make sure your intermediates don't overflow, as you've fixed here.
 

We can also fiddle with our scaling factors, keeping in mind that the fundamental resolution is 1 part in 1024.  I use a spreadsheet to test scaling factors, looking for (in this case) an error of less than 1/1024.  If I can't find a scaling ratio that keeps my intermediates in 16 bits (which I can't in this case), then I look to 32-bit integers with a scaling factor that allows a denominator which is a power of 2 (thus just shifts rather than an integer divide).  I especially look for a denominator of 2^16, so that the compiler will (depending on which compiler and which version) simply lop off the two low bytes of the 32-bit intermediate, yielding a 16-bit result with no division or shifting.  In this case, Numerator = 488/100 * 65536 (calculating outside of the program, so no integer truncation issues).  This gives Numerator = 319,816.  We confirm that the intermediate multiply won't overflow by testing 1024*319816 ~= 327 million, well below the 32-bit limit of 2^32-1 or ~4 billion.
 

You can also just calculate base-2 logs and add to make sure the intermediate log2 is < 32.  log2(319816) ~= 18.3.  log2(1024) = 10.  18.3 + 10 = 28.3, well below 32.  Verifying the result, 2^28.3 ~330 million, matching (within rounding) the 327 million calculated above.
 

So I would calculate the result as follows
  miliVolts = (ADCreading * 319816UL) >> 65536;

EDIT: miliVolts = (ADCreading * 319816UL) >> 16;  // Sheesh!

Last Edited: Fri. Dec 15, 2017 - 08:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kk6gm wrote:
miliVolts = (ADCreading * 319816UL) >> 65536;

 

uhh, how about >>16 ???

 

Jim

 

 

 

FF = PI > S.E.T

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry to divert this thread with a question; however, since an ADC reading of 1023 represents full scale (using 5.0V) then shouldn't the factors be based upon 5.0V/1023 rather than 5.0V/1024? I do realize this is really nitpicking, as the difference is less than 1 part in 1000, AND the ADC has an accuracy of ~2LSB, but ...

David

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

frog_jr wrote:
Sorry to divert this thread with a question; however, since an ADC reading of 1023 represents full scale (using 5.0V) then shouldn't the factors be based upon 5.0V/1023 rather than 5.0V/1024?

I always get confused on that one myself.  There are some fairly extensive threads on that from over a decade ago.  From memory:  5.0V really can't be measured with 5V reference so it is 5/1024.

 

Getting back to my mentioned "perfect" ADC:  It would be rare to have even a near-perfect ADC subsystem.  (and a waste on an AVR8 with a relatively slow 10-bitter?)  So if /1023 or /1024 gives an easier transfer function -- just use it.  IME it ain't always the absolute value as long as bigger keeps getting bigger and vice versa.

 

Which leads to:

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kk6gm wrote:
keeping in mind that the fundamental resolution is 1 part in 1024. I use a spreadsheet to test scaling factors, looking for (in this case) an error of less than 1/1024. If I can't find a scaling ratio that keeps my intermediates in 16 bits (which I can't in this case), ...

I have a brute-force PC program for that. ;)

 

Lessee -- if our target is millivolts and we use the earlier 5000/1024 => 4.8828125 for millivolts,

4.875 7.8125E-003 ==> 39 / 8
4.88888888888889 6.0764E-003 ==> 44 / 9

 

While not strictly passing your criterion, it is less than 1.5 parts of 1/1024 right?  Close enough.  Your 5.0V won't be that close.  IME -- one takes such approaches in e.g. a fullish Mega48 app.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

kk6gm wrote:
miliVolts = (ADCreading * 319816UL) >> 65536;

 

uhh, how about >>16 ???

 

Jim


Well, if you're going to be picky...  

That one's definitely going on my permanent record. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

kk6gm wrote:
keeping in mind that the fundamental resolution is 1 part in 1024. I use a spreadsheet to test scaling factors, looking for (in this case) an error of less than 1/1024. If I can't find a scaling ratio that keeps my intermediates in 16 bits (which I can't in this case), ...

I have a brute-force PC program for that. ;)

 

Lessee -- if our target is millivolts and we use the earlier 5000/1024 => 4.8828125 for millivolts,

4.875 7.8125E-003 ==> 39 / 8
4.88888888888889 6.0764E-003 ==> 44 / 9

 

While not strictly passing your criterion, it is less than 1.5 parts of 1/1024 right?  Close enough.  Your 5.0V won't be that close.  IME -- one takes such approaches in e.g. a fullish Mega48 app.

 

Yes, my spreadsheet also picked out those two ratios as good ones within a 16-bit intermediate result.  I didn't go back and double-check the results, but the spreadsheet claims that 44/9 is off by 1.9/1024, and 39/8 is off by 1.0/1024.  Furthermore, the divide-by-8 can be replaced by 3 right shifts.
 

The key point that you bring up is that it pointless to (pretend to) be more accurate than the physics allows.

Last Edited: Fri. Dec 15, 2017 - 09:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kk6gm wrote:
That one's definitely going on my permanent record.

[Dean Wormer's plotting to get rid of [kk6gm] ]

Greg Marmalard: But [those 'Freaks] are already on probation.

Dean Vernon Wormer: They are? Well, as of this moment, they're on DOUBLE SECRET PROBATION!

(Dean Wormer is obviously a tool of the Evil Empire)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

kk6gm wrote:
That one's definitely going on my permanent record.

[Dean Wormer's plotting to get rid of [kk6gm] ]

Greg Marmalard: But [those 'Freaks] are already on probation.

Dean Vernon Wormer: They are? Well, as of this moment, they're on DOUBLE SECRET PROBATION!

(Dean Wormer is obviously a tool of the Evil Empire)

Road Trip!