tempAmplified = (long) 10000*100*(1.10/1024)*ADCT : Pls clarify!

We are building, as a part of Lab work, a Temperature Meter using UNO, LM35, and 4-digit 7-seg display unit. Everything is fine except that we don't have clear understanding why we need to insert (long) to the RHS of the formula tempAmplified = (long) 10000100(1.10/1024)*ADCT in order to get the correct result. Please note that we are migrators from Assembly Language Platform to Embedded-C Platform.

byte ccArray[10] = {
  0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
                   };

  unsigned int ADCT;                     //ADCT is updated by analogRead(A5) function
  unsigned long tempAmplified;      
  byte x = 0b111111;                   //control cc-pins of the display devices
  byte y; 
  byte indexArray[8];                   //d7d6 (d5d4.d3d2) d1d0

void setup() 
{
  //Serial.begin(9600);
  DDRD = 0xFF;
  DDRB = 0xFF;
  analogReference(INTERNAL); //1

  TCCR1A = 0x00;
  TCCR1B = 0x00;
  TCNT1 = 0x48E5;   //3-sec Time Tick
  TCCR1B = 0x05;    //TC1 run with CLKTC1 = sysCLK/1024 15625 Hz
}

void loop() 
{
  ADCT = analogRead(A5);
  tempAmplified = (long)10000*100*(1.10/1024)*ADCT; //10+E4 times amplified
  getIndices();            //getting indices for the decimal digits of the decimal value of the binary result 
  displayRefresh();     //recurrent refresh of the multiplexed display
  timeDelay3sec();     //user defined time delay function. Arduino's delay(3000) does not help.
}

void getIndices()
{
  for (int i = 0; i<8; i++)
  {
    indexArray[i] = tempAmplified % 10;
    tempAmplified = tempAmplified / 10;
  }
}

void displayRefresh()
{
  for(int i = 5, j=0; i>=2, j<4; i--, j++)
  {
    PORTD = ccArray[indexArray[i]];   //to show d5d4.d2d1 among d7d6d5d4d3d2d1d0
    y = x;
    bitClear(y, j);
    PORTB = y;
    delay(1);          //delay is required to synchronize digit's positional data and its cc-pin
  }
}

void timeDelay3sec()
{
    while (bitRead(TIFR1, 0) != HIGH)
   {
      displayRefresh();
   }
  TIFR1 = 0x01;             //TC1's overflow flag is cleared
  TCNT1 = 0x48E5;        //3-sec pre-set value is re-loaded
}

Given the datatypes of ADCT and tempAmplified unsigned long / UL is the correct choice.

Both of those numbers will fit into a 16 bit int so that's how they are both treated. Since they are both 16 bit int, the result of their multiplication will also be a 16 bit int.

When two 16-bit binary numbers are multiplied, the result is 32-bit binary; why are you saying that it is 16-bit? Is it a typo mistake?

GolamMostafa:
When two 16-bit binary numbers are multiplied, the result is 32-bit binary; why are you saying that it is 16-bit? Is it a typo mistake?

No, that's not how 8 bit microcontroller CPUs work! They only multiply 8 by 8 to give 16 bits. Anything
more ambitious is handled by the compiler / runtime library.

In C operators return the default integer size always unless one of the arguments is larger. On the ATmega
Arduinos the default integer size is 16 bits, so everything is 16 bit unless you force it otherwise. So
16 x 16 gives 16. 32 x 16 gives 32.

Most implementations of C have 32 bits as the default size, but this is a tiny microcontroller with 2k of
RAM total, everything is chosen for compactness and frugality, hence the default int size is 16 bit.

So 16 x 16 gives 16. 32 x 16 gives 32.

In assembly language, we extended the basic '8-bit x 8-bit' multiply instruction of ATmega32A to accomplish multiplication of other higher data sizes through the process of shift and addition.

Based on your post, I performed few simple exercises (given below), and I have come to understand that the casting what you have said forcing provides the required result through the same process of shift and addition. Many many thanks for explaining the issue in a simple language.

//0x7123 * 0xA678 = 0x4991 BA68
uint16_t z1 = 0x7123;
uint16_t z2 = 0xA678;  

uint32_t z3 = z1*z2;
Serial.println (z3, HEX);    //prints: BA68   (Correct!)

Now, forcing the Compiler/MCU to produce full 32-bit result through casting

uint32_t z3 = (uint32_t) z1*z2;
Serial.println (z3, HEX);    //prints: 4991BA68   (Correct!)

As a matter of curiosity, I performed the above multiplication process using do-while loop (repetitive additions) where I declared the data size of the destination as 32-bit long. I found correct result. (When using * operator for multiplication, we need casting; + operator does not require it!)

//0x7123 * 0xA678 = 0x4991 BA68
uint16_t z1 = 0x7123;
uint16_t z2 = 0xA678;

uint32_t z3 = 0;

do
{
  z3 = z3 + z1;
  z2--;
}
while (z2 != 0);
Serial.println(z3, HEX);   //prints: 4991BA68   (Correct!)
tempAmplified = (long) 10000*100*(1.10/1024)*ADCT

The extension of 10000*100 into 32 bits is a non-related issue because the result of that is multiplied by a float, which leads to an implicit type conversion to float. There is no computational advantage here. The compiler can optimize constant arithmetic expressions, so since the end result is a float there is no reason to not use floats:

tempAmplified = (long) 10000.0*100*(1.10/1024)*ADCT

It will compile as

tempAmplified = (long) <float constant>*ADCT

where has the value 10000.0100(1.10/1024).

since the end result is a float there is no reason to not use floats:

It can be found from the display unit of the OP that the required end result is not float at all. We want flat 32-bit unsigned integer from which we will extract eight decimal digits for the decimal value of the 32-bit binary result. We will discard left-most two digits and right-most two digits. The remaining 4-digit represents the actual temperature with 2-digit precision to the right of the decimal point. We have used the casting -- (long) -- to tell the compiler that it has to produce 32-bit integer value regardless of the presence of the floating point numbers in the expression. I think that the expression is evaluated as:

===> tempAmplified = (long) 10000*100*(1.1/1024)*ADCT;
===> tempAmplified = (long) 1074.22*ADCT;
===> tempAmplified = (long) 0x0432*ADCT;

Moreover if we use float temP = 100*(1.1/1024)*ADCT;, we have to look for a function 7seg(temP, 2) to drive the 4-digit 7-segment multiplexed display unit.

Why use float? change
10000100(1.1/1024)*ADCT

to
10000L100ADCT*11/10/1024

the integer truncation still allows 6 decimal digit accuracy, which greatly exceeds the resolution of the ADC.

aarg:
10000L * ...

UL. :wink:

aarg:
...integer truncation still allows 6 decimal digit accuracy...

Adding 5 at the appropriate spot would bump that a scosh. :slight_smile:

aarg:
Why use float? change
10000100(1.1/1024)*ADCT

to
10000L100ADCT*11/10/1024

the integer truncation still allows 6 decimal digit accuracy, which greatly exceeds the resolution of the ADC.

But with the maximum value of ADCT (1023) that overflows a long (even an unsigned long) variable. Divide by 10 earlier in the calculation to avoid overflow:

10000L10ADCT*11/1024