Calculation with different data types

I need to make some calculations as below:

int pulseDuration = 5; // 1/2 cycle in Microseconds
float period = float(pulseDuration * 2) / 1000000; // 1 cycle in Seconds
unsigned int frequency = 1 / period;
int duration = 2734 * period * 1000; // Milliseconds

But I am getting incorrect for frequency.

Kindly spare a few moments and correct me where I am wrong.

Have yo tried printing the intermediate results to see which steps are causing the problem ?

What is the default type of calculations in C unless to force it to use a different data type ?

Yes, I printed the results on the serial monitor.
Only the value of the calculated Frequency is incorrect. I guess, there is something wrong while calculations with different data type.

What is your board?
On the classical arduino the maximum of the int type is 32767
Use unsigned long instead.

1 Like

are you displaying enough digits

5  pulseDuration
0.00001000  period
65535  frequency
27  duration

Please provide a working sketch that we can try.

The Google calculator for the frequency: https://www.google.com/search?q=1+%2F+(+(5+*+2)+%2F+1000000+) = 100kHz
The Google calculator for the duration: https://www.google.com/search?q=2734++(+(5++2)+%2F+1000000+)+*+1000 = 27.34

The 100kHz (100000) does not fit in a 16-bit integers as is used in a Arduino Uno.

A calculation with one or two floating point numbers can be done best with all float variables. Using integers half-way in the calculation is not good in my opinion. If you want a integer at the end, then convert the result to a integer at the end.

If you use all float, then there is no fuzz. When the compiler sees a "5" in the code, then it assumes that it is a integer, when the compiler sees a "5.0" then it is a floating point number. Therefor I prefer to write all the constants as floating pointer number as well.

Here you can see three different options:

void setup()
{
  Serial.begin(115200);

  original();
  allFloat();
  largerIntegersBadExample();
}

void loop() {}

void original()
{
  Serial.println( "Original Code");

  int pulseDuration = 5; // 1/2 cycle in Microseconds
  float period = float(pulseDuration * 2) / 1000000; // 1 cycle in Seconds
  unsigned int frequency = 1 / period;
  int duration = 2734 * period * 1000; // Milliseconds

  Serial.print( "  frequency = ");
  Serial.println( frequency);
  Serial.print( "  duration = ");
  Serial.println( duration);
}

void allFloat()
{
  Serial.println( "All Float");

  float pulseDuration = 5.0; // 1/2 cycle in Microseconds
  float period = (pulseDuration * 2.0) / 1000000.0; // 1 cycle in Seconds
  float frequency = 1.0 / period;
  float duration = 2734.0 * period * 1000.0; // Milliseconds

  Serial.print( "  frequency = ");
  Serial.println( frequency, 10);
  Serial.print( "  duration = ");
  Serial.println( duration, 10);
}

void largerIntegersBadExample()
{
  Serial.println( "Larger Integers");

  long pulseDuration = 5L; // 1/2 cycle in Microseconds
  float period = float(pulseDuration * 2) / 1000000L; // 1 cycle in Seconds
  long frequency = 1L / period;
  long duration = 2734L * period * 1000L; // Milliseconds

  Serial.print( "  frequency = ");
  Serial.println( frequency);
  Serial.print( "  duration = ");
  Serial.println( duration);
}

Try the sketch in the Wokwi simulator:

Result:

Original Code
  frequency = 65535
  duration = 27
All Float
  frequency = 100000.0000000000
  duration = 27.3399982452
Larger Integers
  frequency = 100000
  duration = 27

Note to others: I called the mix of float and long a bad example. When float and integers are mixed, then I have to crunch my brain and check every line if using integers at that point is valid and I'm too lazy to crunch my brains. Number crunching is the job of the compiler and the processor.

1 Like

Int can only store whole numbers. This calculation will result in frequency being 1 or 0.

Actually, it doesn't. Try a calculator to calculate each step and follow what the compiler is doing.
When the compiler sees "1 / period", it sees "integer divided by float". Variable "period" is a float. The compiler will use a floating point calculation for that, since at least one of the variables is a float. Since "period" is much smaller than 1, the result will be 100000, which will be converted to be put in the variable "frequency". That does not fit in a 16-bit variable, so the compiler scratches its head and tries to make the best out of the bad situation.

Did you test it?
OP got 65535

if you are running on a UNO or Mega unsigned int is 16bits (range 0 to 65535)
try unsigned long

void setup() {
  Serial.begin(115200);
  int pulseDuration = 5; // 1/2 cycle in Microseconds
  float period = float(pulseDuration * 2) / 1000000; // 1 cycle in Seconds
  unsigned long int frequency = 1 / period;
  int duration = 2734 * period * 1000; // Milliseconds
  Serial.println(period, 5);
  Serial.println(frequency);
}

void loop() {}

result

12:51:31.223 -> 0.00001
12:51:31.223 -> 100000


1 Like

Division by a very large or small value is always dangerous loss of precision.

I would rewrite the calculations like this:

float period = (float)(pulseDuration * 2) / 1000000; //  <=== are you really need it?
unsigned long frequency =  1000000 / (pulseDuration * 2);
int duration = 2734 * 1000UL  /  frequency; 
1 Like

Yes, "unsigned long" worked.

I appreciate all the participants for their input.
Thanks to all.

We got a little carried away :nerd_face: but sometimes it is interesting and important to know what is going on.

There is more to it. The calculation is done by the compiler and not runtime by the microcontroller. The compiler recognizes the constants and the calculations and calculates the result for you.

For example this sketch:

void setup()
{
  Serial.begin(115200);

  int pulseDuration = 5; // 1/2 cycle in Microseconds
  float period = float(pulseDuration * 2) / 1000000; // 1 cycle in Seconds
  unsigned int frequency = 1 / period;
  int duration = 2734 * period * 1000; // Milliseconds

  Serial.println( frequency);
  Serial.println( duration);
}

void loop() {}

Uses this code to print the values:

 5b8:	4a e0       	ldi	r20, 0x0A	; 10
 5ba:	6f ef       	ldi	r22, 0xFF	; 255
 5bc:	7f ef       	ldi	r23, 0xFF	; 255
 5be:	80 e0       	ldi	r24, 0x00	; 0
 5c0:	90 e0       	ldi	r25, 0x00	; 0
 5c2:	0e 94 6c 01 	call	0x2d8	; 0x2d8 <_ZN5Print11printNumberEmh.constprop.12>
 5c6:	0e 94 ba 01 	call	0x374	; 0x374 <_ZN5Print7printlnEv.constprop.14>
 5ca:	4a e0       	ldi	r20, 0x0A	; 10
 5cc:	6b e1       	ldi	r22, 0x1B	; 27
 5ce:	70 e0       	ldi	r23, 0x00	; 0
 5d0:	80 e0       	ldi	r24, 0x00	; 0
 5d2:	90 e0       	ldi	r25, 0x00	; 0
 5d4:	0e 94 6c 01 	call	0x2d8	; 0x2d8 <_ZN5Print11printNumberEmh.constprop.12>
 5d8:	0e 94 ba 01 	call	0x374	; 0x374 <_ZN5Print7printlnEv.constprop.14>

Do you recognize the 255, 255, that is the integer of 65535 which is put on the stack as a constant value. It is not runtime calculated. And the 27 is also put on the stack.

I got this assembly by putting the sketch in Wokwi. Run it. Stop it. Right click on source code, click "Command Palette" and search for "assembly", click that. A new tab "sketch.lst" shows the assembly code.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.