Hello All,

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

The result is alway zero.

Is there somewhere a tutorial for this subject available?

Thank you in advance

Author

Message

Hello All,

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

The result is alway zero.

Is there somewhere a tutorial for this subject available?

Thank you in advance

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.)

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.

(or something similar.)

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.)

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

Thank you .

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

Now it works finne

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.

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

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)

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

miliVolts = (ADCreading * 319816UL) >> 65536;

uhh, how about >>16 ???

Jim

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:

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.

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.

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

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)

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!