bclose

8 – FUNCTIONS AND INTEGERS

Objectives

 

 

    • Work with integers.
    • Learn to define functions in Arduino C++.
    • Working on logic (and algorithmic) thinking.
    • Different kinds of integers in C++.

 

Bill of materials

Arduino Uno  Arduino Uno or equivalent. We can use any other Arduino board in this chapter.
We will also need a PC computer with the Arduino IDE properly installed and set up.

THE FIRST FUNCTION: CHECKING WHETHER A NUMBER IS PRIME

 

We have already mentioned before, that programming is a bit like riding a bike, you learn to ride by riding and you learn to program… by programming. You have to learn the syntax of the language, C ++ in our case, but also to learn how to solve logical problems and split them into instructions.

Do a programming course is fine but at the end you have to get hands-on programming and have problems to solve, because only solving them, alone or with some help, you can learn. You can’t learn to swim just studying!

With a trembling hand, let’s focus this chapter on some classic examples of programming, such as calculating prime numbers, to train the skill to find out practical algorithms to solve more or less abstract problems and to introduce some additional concepts.

 
  • It is important to note that there is no single way to solve a particular problem and that one does not have to be better than another, but often efficiency or elegance criteria are applied to choose a solution.
 

This chapter will require a little more effort than the previous ones because we will start training a little-used muscle, brain, in a rare task, to think. And this is something that requires some effort, but it is necessary to advance.

Suppose we want to create a programming artefact which returns true or false depending on whether the number we provide is prime or not and to which we can call several times without copying the code again and again. We will call it Prime() and it works as follows: If the number is prime it must return true, otherwise it returns false, that is, it must return a boolean value.

This is what we call a function.

In fact, we have already used several functions that Arduino C++ provides, as Serial.print(), Serial.available(), abs(), etc. They are recognized by the opening and closing braces after the name.

C++ provides all the tools we need to build our own functions, which is very useful because it helps us to split a general problem into pieces or smaller functions easier to handle.

To define a function we have to declare it first and then tell C++ what it has to do. Let’s follow with our example function, Prime():

     bool Prime( int x) // x represents the parameter that will be passed to the function 
         {
                Here goes what the function has to do
                ...
                return( bool);
         }

Note that the first word in the function is bool. This word defines the type of data that is returned when the function is called. In our case, we have defined the Prime() function to return a boolean data type, so we must include at some point either the statement return(true) or return(false) to return the result to the caller. If the function would return an integer we had to define it this way: int Prime( int x).

 
  • If a function does not return any value but simply does its job and just ends, then we have to declare it as void (empty). We already know two of these functions well: setup() and loop().
 

Let’s see how could be the code inside the Prime() function:

     bool Prime( int n)
         {
            for ( int i = 2 ; i <n ; i++)
                {
                   if ( n % i == 0) // If the remainder is 0 then the number is divisible and is not prime.
                       { 
                          Serial.println ( String(n) +" is divisible by :  " + String(i)) ;
                          return(false) ;
                       }
                 }
           return (true) ;
 }

To find out whether a number n is prime or not, we only have to divide it by all positive numbers greater than 1 and less than n. In the example, we divide the number n by all positive numbers ranging between 2 and n-1.

If we find out, inside the for loop, that the remainder of n modulo i (n % i == 0) is equal to zero, then the number n is divisible by i and is not a prime number. That implies that we must return false to the instruction that called the function.

If no divisor is found, the function only has to return true after the for loop. This is called a brute force method and can be certainly improved, but it works at the moment.

To use the Prime() function we have to pass it an integer as argument, int n. An argument or parameter is the data that we pass to the function so that it can perform its task. Remember that when we defined the function we wrote: bool Prime(int n), where n represents the number we want to check.

Let’s write the loop() function to check whether it works:

Sketch 8.1
     
          void loop() 
          { 
              int x = 427 ; // The number to test
              bool p = Prime(x);
             if (p )
                 Serial.print( String(x) + " is prime.") ;
             else
                 Serial.print( String(x) + " is not prime." ) ;
          }

Let’s see how many primes there are up to 1024.

Sketch 8.2
      bool control = true ; 
      int maximum = 1024 ;

      void loop()
      { 
         if (control) // This control variable avoids that the if block repeats again and again 
         { 
            Serial.println( "These are the prime numbers up to " + String( maximum)) ;
            Serial.println( "-----------------------------------------");
            Serial.println("Prime number position = \t1: \t Prime number = 1"); // 1 is the first prime number but we don't use it later, of course 
            int counter = 1;
            for ( int x = 2 ; x < maximum ; x++)
            { 
               bool p = Prime(x);
               if (p){ 
                  counter++;
                  Serial.println("Prime number position = \t" + (String)counter + ": \t Prime number =\t" + x) ; 
               }
            }
         } 
         control = false ;
      }

      bool Prime( int n)
          { 
              for ( int i = 2 ; i <n ; i++)
                   { 
                        if ( n % i == 0) // If the remainder is 0 then the number is divisible and is not prime.
                        return(false) ;
                   }
              return (true) ; // If this statement is executed then we have not found any divisor and the number is prime
          }

Although the program works correctly, the output is not very compact (remember that we like to be stylish). Let’s give the output a more appropriate format by using the tab character (the tabulator), which is represented as ‘\t’ , and a comma.

Sketch 8.3
     
     bool control = true ; 
     int maximum = 1024 ;
     int counter= 1 ;

     void loop()
          {  
              if (control)             // This control variable avoids that the if block repeats again and again
                   {
                        Serial.println( "These are the prime numbers up to  " + String( maximum)) ;
                        for ( int x = 2 ; x < maximum ; x++)
                              {
                                 if (Prime(x) )
                                    if (counter++ % 8 == 0)
                                         Serial.println(String(x)+"," ) ;
                                    else
                                         Serial.print(String(x) +","+ '\t') ; 
                              } 
                   }
              control = false ;
          }

Now the program formats the output in a slightly more presentable and comfortable way to be read.

Prime numbers table

To achieve this, we have added a comma and a tabulator after each number except one of every 8, the last, because we have used rows that contain eight columns. When we reach the eighth column, instead of using Serial.print() we do use Serial.println() because this way C++ inserts also a new line and a carriage return, a line break.

We should comment also the following line:

if (counter++ % 8 == 0)

When we write two plus symbols after the name of a variable, ++, that indicates C++ that it must use the current value of the variable and after that increase its value by 1. Technically speaking, these two plus symbols are called the post-increment operator.

We could also have written the instruction this way:

if (++counter % 8 == 0)

In the last instruction C++ increases the value of the counter variable by one before using it. This notation is quite common in C++ and we should recognize it. We call the two plus symbols before the name of a variable the pre-increment operator.

There is another pair of operators that can be used to decrement the value of a variable (counter in our case): the pre-decrement operator, –counter, and the post-decrement operator, counter–.

THE INTEGER DATA TYPE

 

This would be a good time to wonder how much could our integer variable grow up in the previous program. We assigned it a value of 1024, but does the integer data type have a size limit?

The answer is yes. The integer data type in Arduino C++ uses 16 bits, so the maximum value we can get would be 216 -1= 65.535. There are 65.536 values but we must count also the zero, so the maximum value we can get is 65.535. But as the integer data type is signed, the values range from -32.768 to +32.767.

In fact, Arduino C++ provides several data types to handle integer numbers:

DATA TYPE DESCRIPTION RANGE OF VALUES
int Signed integer, 16 bits from -32,768 to 32,767; (216 – 1)
unsigned int Unsigned integer, 16 bits from 0 to 65.535; (216 – 1)
long Signed integer, 32 bits from -2.147.483,648 to 2.147.483.647; (232 – 1)
unsigned long Unsigned integer, 32 bits from 0 to 4.294.967.295; (232 – 1)
byte Unsigned integer, 8 bits from 0 to 255; (28)

All these data types represent signed and unsigned integers and can be used to work with really big numbers, but all of them have a size limit.

In fact, C ++ has the nasty habit of waiting for us to take care that we do not put a value that does not fit into a variable. When this happens it is called overflow, and C ++ completely ignores the issue, leading to problems difficult to detect if one does not walk gingerly.

Try this:

int i = 32767 ;
Serial.println(i+1);

You see immediately that if i is equal to 32767 and we increase its value by 1, C ++ interprets it as a negative result. That is because C++ simply does not control overflow.

Let’s try also the following operation:

int i = 32767 ;
Serial.println(2*i + 1);

According to Arduino, the result is -1.

 
  • This is not a mistake, it was so decided. C ++ does not control overflows, so be very careful, because this kind of mistakes can be very difficult to detect.

 

MORE ON FUNCTIONS IN C++

 

 When we declare a function we must specify which kind of data type returns. Let’s see some examples :

Example Description
int Function1() Returns an integer
String Function2() Returns an String
unsigned long Function3() Returns an unsigned long
void Function4() Does not return any value

One function can return any data type defined in C++ but only a single value each time, if we use the return() statement . It is specifically not allowed to return more than one value. If this is required, there are other solutions that we will further see.

 
  • We can deal with this problem by using global variables or passing values by reference. We will discuss it in further chapters.

 

What it is in fact allowed is to pass several arguments to a function:

int Function5 ( int x , String s , long y)

In this example we have declared a function, Function5(), that have three arguments: an integer, a String and a long, respectively.

Summary

 

 

    • We have defined our own function to find out whether a number is prime.
    • We have seen that the integer data type has a size limit.
    • We have met data types with different ability to handle more or less large integer numbers, but all of them still have a size limit.
    • Data type overflow is a key concept and should be taken into account when we work with integers.
    • We have been playing with logical problems and we have seen some solutions that can help you find some of your own.

 

 

No Comments

Give a Reply