Skip to content
Physics and Astronomy
Home Our Teaching Resources C programming Variables and expressions
Back to top
On this page
Contents

More about variables, expressions and assignment

Arithmetic expressions

Arithmetic expressions use the symbols "+ - * /" for the arithmetic operations plus, minus, multiply and divide. They have the standard arithmetic precedence (*/ bind more closely than +-) and left-to-right evaluation. Parenthesis () have the usual grouping effect. The following examples expressions on the same line have the same value to within rounding errors:

x * y + z (x*y) + z
1.5 - x - y (1.5 - x ) - y
x/y/z (x/y)/z x/(y*z)

Having calculated an expression we can use it as the argument to a function such as: sqrt(x*x+y*y)

No prizes for guessing what the sqrt() function does! (We shall see below there is also a useful function hypot()). The most common use is probably the one we one we showed above, putting it on the right-hand side of "variable = ".

Example: height of a plane

If we are writing a program dealing with a plane of gradient 1 in both the x and y directions (i.e. an absolute gradient of sqrt(2)), and we want to know the distance of a point on the plane from the origin, then our code might look like this (where we have added a couple of "interesting" lines at the end):

// Calculate the height of a plane of gradient 1
// in both the x and y directions.
#include <stdio.h>
#include <math.h>
int main() {
  double x, y, z, distance;

  x = 2.1;
  y = 1.3; 
  z = x + y;
  distance = sqrt(x*x + y*y + z*z);
  printf("Distance; %g\n", distance);
   
 // See what happens now
  x = 10.0;
  y = y + x;
  printf("Distance; %g\n", distance);
  
  // More code here...
  return 0;
}
Step through this code


Remember: z will not be exactly 3.4 because the computer works in binary and neither 1.3 , 2.1 , or 3.4 are expressible in a finite number of "binary places".

An operator is something that takes one or more arguments and produces a result.

Mathematical operators have the same precedence (bind as closely) as normal arithmetic.

  1. Step through the above "Key example".
  2. To demonstrate assignment
  3. Step through the above "Key example" in a new window.
  4. Press "Start program" and see the variables spring into existence with random value.
  5. Step through the code one step at a time stopping just before the second assignment of x (x = 10.0)
  6. Notice the extremely simplistic way the computer performs calculations, and stores and retrieves the values.

Arithmetic assignment

It is essential to realise that the statements above with equals signs such as:

 z = x + y;

are one-off arithmetic assignments, not lasting mathematical relationships. (Remember, we are dealing with algorithms which are sequences of instructions or actions to achieve the desired result.)

When it gets to the statement "x = 10.0;" the compiler will not think to itself "z equals x + y, so z is now equal to 11.3 and I must recalculate the distance". The assignment of z was a one-off action so z will remain (approximately) equal to 3.4 until we explicitly change it, and the same applies to distance.

Of course the compiler doesn't "think" at all and it certainly doesn't realise the significance of the word "distance". For all it cared we could have called the four variables (x, y, z and distance)   "variable1", "variable2" , "variable3" and "i_feel_like_a_banana". But the program would have been much less clear to us.

  1. Continue stepping through the above example
  2. To demonstrate that assignment is an action not a relationship and has no side-effects
  3. Continue stepping through the above code changing the values of x and y.
  4. Notice how the value of z does not change.

Statements such as: z = x + y; are one-off arithmetic assignments, not lasting mathematical relationships.


When the value of a variable changes its old value is gone forever

If we change the value of a variable there is no way to retrieve it later. For example, in the following code the original values of the variables materials and labour are lost when we assign them new values:

#include <stdio.h>

int main() {
  double materials, labour, total_cost;

  materials = 9.4;
  labour = 11.3;
  total_cost = materials + labour;
  printf("The first cost is %g\n", total_cost);

  materials = 14.3;
  // The old value  of materials has gone forever

  labour = 21.2;
  total_cost = materials + labour;
  printf("The second cost is %g\n", total_cost);
  return 0;
}
Step through this code

When the value of a variable changes its old value is gone forever

  1. Change the value of mass or velocity in your momentum calculation
  2. To illustrate that changing the value of a variable does not automatically change the values of variables previously calculated from it
  3. Find your "momentum" exercise from the last lesson.
  4. Copy-and-Paste the printf() statement that prints out the mass, velocity and momentum so that you now have two copies, on two separate lines. Build & run and check that it does print them out twice.
  5. In between these two lines insert a line that changes the value of either the mass or velocity (or insert two lines and change hem both).
    • What does your program produce now?
    • Does the second value of the momentum reflect the new value of mass and/or velocity?
    • Do you understand why/why not? If not, step through the example above again.
  6. Finally, copy the line with the momentum calculation and paste it into the correct place for it to update the momentum to the correct new value.
    • What is the corrrect place to put this second calculation? Think through the steps the program will take and do the calculation when the mas and/or velocity will have the new values.

Standard mathematical functions

As mentioned in the previous lecture, putting the the following compiler directive at the top of our file:

#include <math.h>

(note the Americanism it's math not maths) gives us access to all of the usual mathematical functions.

We shall see below that the include file complex.h gives us access to complex numbers and functions so we show the complex versions here for convenience.

Some useful mathematical functions

Real function(s)
math.h
Complex version
complex.h
Notes
sin(x) cos(x) tan(x) csin(x) ccos(x) ctan(x) in radians
asin(x) acos(x) atan(x) casin(x) cacos(x) catan(x) inverse sin (arcsin), etc.
hypot(x, y)   hypotenuse: √ x*x + y*y )
atan2(x, y)   "whole range" atan(), (see note)
sinh(x) cosh(x) tanh(x) csinh(x) ccosh(x) ctanh(x) hyperbolic sine, etc.
asinh(x) acosh(x) atanh(x) casinh(x) cacosh(x) catanh(x) inverse hyperbolic sine (arcsinh), etc.
sqrt(x) csqrt(x) square root
log() log10() clog() clog10() natural log, log to base 10
pow(x,y) cpow(x,y) xy Note: do not write x^y
fabs(x) cabs(x) floating-point absolute value and complex absolute value

All arguments and returned values are doubles for the real functions and double complex for the complex functions except that cabs() returns a double.

Be sure to use the right one.

Note: atan2()

The function atan2(x,y) returns the number a such that sin(a) = x / r and cos(a) = y / r where r = √ x*x + y*y  . This is usually better than using atan(x/y). Think of it as the angle from the vertical y axis, twelve o'clock.

#include <math.h> lets us use sin(), etc.

  1. Mathamatical functions
  2. To practice using built-in mathematical functions.
  3. Create a new on-line program in a new window with a short comment at the top saying it demonstrates using mathematical functions.
  4. Immediately after your program includes <stdio.h> include <math.h>. You will need to do this on a separate lines:
    #include <stdio.h>
    #include <math.h>
    
    • As always, type this in, don't copy and paste. Typing it in will help fix it in your mind.
  5. Declare a double variable called x. Give it a positive value less than one.
  6. Print out the value of its sine, square root, and hyperbolic sine of x along with a few other mathematica functions.
  7. Try printing the value of sin(asin(x)). Does it do what you expect?
  8. Try calling the asin() function with suitable values to answer the question:
    • Does asin() return values in the range 0 to π or -π/2 to π/2?

Warning: don't confuse fabs(), cabs() and abs()

C also provides an integer absolute value function abs() defined inside stdlib.h as well as fabs() and cabs() .

Don't confuse fabs(), cabs() and abs()<

  1. abs() and fabs()
  2. To see the difference between abs() and fabs().
  3. Create a new program with a comment at the top saying it shows the difference between the abs() and fabs() functions.
  4. Immediately after your program includes <stdio.h> include the two files <math.h> and <stdlib.h>. As in the previous mini-exercise you will need to do this on separate lines:
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    • As always, type this in, don't copy and paste. Typing it in will help fix it in your mind.
  5. Declare a double variable, say x with a positive, non-integer value.
  6. Print out the values of fabs(x) and abs(x) and notice the difference.
  7. Now make x negative and again see what happens.
  8. Remove the inclusion of <math.h>. One easy way to do this is just to "comment it out":
    // #include <math.h>
    
    This just turns the line into a comment, which is ignored.
  9. Build and run.
  10. Now a weird one: do the same for <stdlib.h>.
     
    Giving functions the wrong type of arguments can have unexpected effects. We shall see in a later lecture how C allows us to avoid this.

Warning: use pow(x,y) not x^y

Some languages use ^ for exponentiation, C uses it for something else

Write x*x not x^2 and pow(x,y) not x^y

Sensible variable names

It is extremely important to give variables fairly short names that tell us what they represent. For our earlier "momentum" example this doesn't mean huge names such as mass_of_the_object_measured_in_kilogrammes, velocity_of_the_object_measured_in_metres_per-second etc but it does mean mass, velocity and momentum (still actually a bit long) or mass, vel and p or even just m, v and p.

Choosing a sensible variable name is a compromise between clarity and compactness. For example, if variables are called number_of_atoms and maximum_number_of_atoms, that's not just a lot to type but any arithmetic expressions involving them and other similarly-named variables become very hard to understand. This is particularly a problem for scientific programs with large numbers of complicated arithmetic expressions.

Try to develop a consistent short-hand. One popular scheme is to either capitalise the second and subsequent "word" in a variable name, (sometimes referred to as "Camel-case" as the variable name ends up with "humps") or to join them together with the underscore character. Or just run them together...

Examples for molecules, atoms and electrons

Suppose we want variables for the numbers of electrons, atoms and molecules and for the maximum number of electrons, atoms and molecules the system can handle. We might choose:

Underscore

  int n_atoms, max_atoms, n_mols, max_mols, n_elecs, max_elecs;
 

Camel-Case

  int numAtoms, maxAtoms, numMols, maxMols, numElecs, maxElecs;
 

Notice how the initial letter is still in lower case: it's very easy to mistype numAtoms as NumAtoms and surprisingly hard to spot.

Run together

  int natoms, maxatoms, nmols, maxmols, nelecs, maxelecs;
 

Avoid variable names that need comments

On the other hand, avoid short but unhelpful variable names such as in the following example.

Example

Suppose we are unfortunate enough to have to deal with masses in both kilograms (kg) and imperial pounds (lb). (Naturally we should avoid this situation if at all possible!)

A poor choice would be something like:

  double mass1; /* Kilograms */
  double mass2; /* Pounds */

Try to write your program so that it explains itself without needing any comments, and only use comments inside of functions for situations where that isn't possible.

When we come to use these variables we will constantly be wondering which is which and referring back to the comment where the variables were declared. Much better would be:

  double mass_kg, mass_lb;

which doesn't even need a comment.

Use short variable names that mimic the words you would use when describing the task to somebody else, such as mass, price, n_students etc.

Integer variables and expressions

Expressions have both a value and a type.

It's useful to think of type names such as double and int as primarily being adjectives rather than nouns. When used as a noun it's implicit from the context whether double means a double variable, a double expression or even a double constant ("2 is an int, 2.0 is a double.")

As we mentioned last week, some Some things, such as people, are conventionally considered to considered as coming only in units of integers, not fractions. Almost every C program will have such integer variables and expressions, which have generic type int. Apart from a little discussion of what happens when we try to give them non-integer values, including dividing one integer by another, they are fairly simple and obvious.

Floating-point variables are used for things we measure and integer variables are used for things we count. Make a variable an integer only if is it logically impossible for it to have a fractional value.

Types of integer variables

It's good to know that 210 is approximately equal to 1000. It's also good to know that it's not exactly equal to 1000. Use the prefixes Kibi, Mibi, Gibi, etc. if it's import to be precise. (Which is not very often!)

The number of bytes used for integers is much less standardised than for floating-point numbers. Four bytes for an int is common and we shall occasionally use it in our examples, but if it matters to you be sure to check A four-byte int can store integer values roughly in the range plus or minus 2 billion (231).

Since that is not always enough C also provides eight byte integers, giving a range of roughly plus or minus 8 billion billion or 263.

One bit of the integer is used to indicate whether it's positive or negative. We may add the qualifier "unsigned" before any integer type to regain the extra bit in which case the value are always positive.

C also defines a specialised integer type called char which is used for storing printable characters such as 'a', 'b', 'c', etc. We shall meet this type in a later lecture.

Declaring and using integer variables

Integer variables are declared in the obvious manner:

  int j, k;

  j = -2;
  k = 4 * j - 11;

Notice that the specific types are called int and long, the word "integer" is a generic term, there is no variable type of this name.

Initialisation

Variables of all types (not just integers) can be initialised at the same time as being declared so we could have written the above code as:

  int j = -2, k;

  k = 4 * j - 11;

Notice that k was left uninitialised (which is a fancy word for random!) when it was declared. It would have been Very Bad News to have tried to use the value of k without explicitly setting it first.

Uninitialised variables of all types have random values and are not automatically set to zero.

  1. Unused and uninitialised variables
  2. To help us handle two common errors.
  3. We look at how the compiler deals with two common situations where variables are declared but not used. As an example we shall consider people travelling in cars.
  4. Start a new project. Put a brief comment at the top saying this is calculating how many people to put into cars.
  5. Declare two variables suitable for storing the number of people travelling and the number of cars they have. As always ask yourself the questions we asked ourselves before:
    • Is it logically possible for people and cars to have non-integer values? If it is make them a double, if it is not make them an int.
    • Now ask: "can I think of two short, snappy variable names so that when I look at them having read the comment at the top of the program I will immediately know which is the number of people and which is the number of cars?". Use those names for your variables.
  6. Now you have two legally-declared variables that you have not yet used, press "Build and Run". What happens?
  7. Print out the values of these variables (using printf() and %d) even though you have not yet given them a value. See what happens, both in terms of the compiler messages and the output.
     
    When we do something that is legal C but probably indicates we have made a mistake the compiler will warn us but but will carry on.
  8. Change the default: Check the "Warnings as errors" box and see what happens when you Build with this option enabled..
  9. This option can be quite useful as our programs get larger and we enable it by default on our PCs.

Integer constants

As shown above, these work as we would expect but any Any constant with a decimal point or an exponent is treated as a double even if its value is an integer. This means that 1000.0 and 1e3 are both doubles with value "one thousand point zero". This only really matters when we are dealing with division, which we discuss in the next section.

for historical reasons, integer constants starting with zero are treated as octal (base eight), not decimal. So 017 is fifteen, not seventeen.

Something that is occasionally useful is that integer constants starting with "0x" are treated as hexadecimal ("hex") or base sixteen, using the letters "a-f" for ten to fifteen. This is useful as sixteen is 2 to the power 4, i.e. four bits so any byte can be represented by two hexadecimal "digits". Hex is used to specify colours on the Web, for example.

Printing the values of integer expressions

We saw in the last lecture that %g is used to print the value of a floating-point expression. For printing the value of an integer expression we may use either %i or %d (where the d stands for decimal).

  int j = 6, k = 7;
  printf("j+k is %i, j-k is %i\n", j+k, j-k);

Use %g in printf() to print a floating-point number. Use %i or %d (decimal) for ints.

Using the wrong format

Using the wrong format, for example %d for a double %g for an int will have bizarre and unexpected effects. Tecnically "the behavior is undefined" which is one of the most ominous phrases in computing.

  1. Right and wrong formats
  2. To show the dangers of using the wrong format
  3. Create a new on-line program in a new window, with a suitable title and opening comment.
  4. Declare two variables, one an int and the other a double and give them each a suitable value.
    • "Suitable" means a non-zero value for the integer and a non-integer value for the double with the two values being "fairly different".
  5. Now print them out correctly using %d (or %i) and %g respectively.
  6. Build and run and check the output is correct.
  7. Now see what happens when you use the correct formats but the wrong way round and when you use "%d %d" and "%g %g".
    • The only understandable part of what happens should be the warning message.
    • Don't try looking for any logical reason for what does: there won't be one!
  8. Check the "Warnings as errors" box. and try again.
    • This is why we enable this option by default on our cluster PCs.

Integer division and remainders

Warm-up questions

  • You wish to divide 13 gallons of petrol equally between 4 cars. How many gallons of fuel go into each car?
  • You wish to divide 13 people equally between 4 cars. How many people go into each car?
  1. Answer the above questions.
  2. Should become obvious!
  3. Answer the above questions.
    • Do you get the same answer each time?
    • If not, why not?
    • If you do, what will be the result?

Why are we interested?

For the questions above you should have given a fractional answer for the first question but the second should have had the form "x people per car with y left over". You may even have gone one step further and used those numbers to say "there will be m cars with x people and n with x+1".

Whenever we dealing with the division of integers we need to decide whether our algorithm requires the division to be treated as an integer (plus a remainder) or as floating-point number. C gives us the chance to do either integer division, which always results in an integer (and the ability to find the remainder), or to explicitly convert the integers to their floating-point values. The key is to make sure we make the right choice: it is not good to have 3.25 people per car!

As humans we instinctively realise whether to treat a division as integer (plus a remainder) or as fractional, when programming we must explicitly say which we want. Failure to do this is a common cause of mistakes.

Integer expressions result in integers

Any arithmetic expression involving only integers is treated as an integer expression, for example:

// Integer and floating-point division 
  int j = 5, k = 3;
  double x;

  x = (j + 7 ) / (k*6 -j*4 + 12);
Step through this code

 

here x will have the value 1, not 1.2 . For simplicity we will use the example of the division of two integer variables below, but exactly the same rules apply to any integer expressions.

For the rest of this lesson  we shall assume that variables j and k have integer type and that x is a double.

Integer division

Integer division of positive expressions is straight-forward and is by far the most common case of integer division. (We don't often want to divide minus-thirteen people between four cars!) In this section we shall consider only positive values of j and k.

If both j and k are positive ints the result of the division j/k is the smallest integer less than or equal to the true, floating-point value of j/k. This can be thought of as "the number of whole ks in j".

   7/2  equals 3   (with remainder 1)
  13/5  equals 2   (with remainder 3)
   9/3  equals 3   (with remainder 0)

Note that this is not necessarily the nearest integer, for example 13.0/5.0 equals 2.6 which is nearer to 3 than to 2 but 13/5 equals 2, not 3. This can equally be thought of as "discarding the fraction" or rounding towards zero.

Division of two integer expressions gives an integer result and throws away the fraction, eg 5/3 is 1. It does not produce the nearest integer.

Division of negative integers

Division of negative integers is extremely rare and is defined by the rule:
-j / k == j / -k == - ( j / k )

(see the appendix).

% is the integer remainder operator

When we want to find the remainder we can use the expression j % k "j remainder k" (as above, k must not be zero), defined by:

j % k equals   j - (j / k) * k

e.g, using the same examples as above:

   7 % 2  equals 1
  13 % 5  equals 3
   9 % 3  equals 0

This means that j%k is always in the range 0 to k-1.

Between them integer division and the remainder operator enable programs such as:

Example: convert seconds to minutes and seconds

//
// Convert a total number of seconds to minutes and seconds
//
#include <stdio.h>


int main() {
  int totaltime, seconds, minutes;

  totaltime = 429;
  seconds = totaltime; // Keep the value of totaltime as a check

  minutes = seconds / 60;
  seconds = seconds % 60;

  printf(" %i seconds in total, is %i minutes and %i seconds.\n",
   totaltime, minutes, seconds);
  return 0;
}
Step through this code


The output is:

  429 seconds in total, is 7 minutes and 9 seconds.

You will observe the use of "%i" to print an integer. Strangely, "%d" (for decimal) is a synonym for %i (the "d" does not stand for "double"!). Use "%ld" or "%li" to print a long.

  1. Step through the above example.
  2. To observe integer division and remainder.
  3. Step through the above example.
  4. Click on "Start program".
  5. Before the assignment of minutes use the law of integer division to predict the value of minutes.
  6. Step forward and check you were correct if not work out why.
  7. Before the assignment of seconds use the law of integer remainder to predict the value of seconds.
  8. Step forward and check you were correct if not work out why.

% is the integer remainder operator, eg 5 % 3 is 2.

  1. Dividing people between cars
  2. To familiarise ourselves with integer division and remainder.
  3. Take your previous mini-exercise and this time give sensible positive values to the number of people and the number of cars.
  4. Use the / and % operators to solve the "dividing people between cars" problem to print out something like:
     
    11 people between 3 cars is 3 people per car with 2 people left over.
     
    There is no need to worry about minor grammatical problems such as: "1 people left over".
  5. Try various positives values for the number of people and of cars to check that / and % are behaving as you expect.

Tips and warnings concerning integer division

Integer division is simple but we don't want it to occur by accident, when we were expecting floating-point division.

As scientists our biggest danger from integer division is that we do it by accident when we meant to do floating-point division.

1. Always use decimal points for double constants with integer values.

If we do this all the time, even in expressions not involving integer division, we will avoid mistakes like:

  x = 2/3;              /*  Oops! */
  x = 2.0/3.0;          /*  Good! */

Always use decimal points for double constants with integer values.

2. The choice of integer or floating-point division depends on the expression, not the context.

If as humans we look at the statement:

  x = j/k;

we might be tempted to look at the left-hand side, see it's a double and treat the right-hand side j/k as a floating-point division. (This is a particular danger because we see the x first.) Computers don't work that way - they follow fixed rules:

  1. Evaluate j/k (as an integer)
  2. Assign the result to x

The right-hand side is evaluated as an integer and only after that does the compiler look at the left-hand side. The computer doesn't then say "x is a floating-point variable, John probably wanted to evaluate j/k as a floating-point expression so I'll go back and do it for him".

The choice of integer or floating-point division depends on the expression, not the context.

3. Don't put integer division inside floating-point expressions.

  x = v * (j/k);  

If v is integer then j/k will be evaluated as integers, if v is floating point j/k will be evaluated as floating-point! It's just giving ourselves another chance to go wrong.

Don't put integer division inside floating-point expressions.

A steppable example

We gather a few cases of integer division in the following example. The final expression shows what happens when we replace the integer "3" with the floating-point number "3.0".

Keep a close eye on the type of each expression, particularly during division.

// Demonstrate inadvertant integer division
#include <stdio.h>
int main() {
  int j, k;
  double x;

  // Simple division of two  integer:
  x = 7/3;
  printf("x: %g\n", x);

  // Same but with variables:
  j = 7;
  k = 3;
  x = j/k;
  printf("x: %g\n", x);
 
  // More-complicated case:

  x = 1.14159265358979 + j/k;
  printf("x: %g\n", x);

  // The reverse just throws away the fraction:
  k = x;
  printf("k: %d\n", k);

  
  // See what happens when we put "3.0":
  x = 7/3.0;
  printf("x: %g\n", x);

  return 0;
}
Step through this code


A previous example revisited

We can revisit the earlier example of integer division:
 
x = (j + 7 ) / (k*6 -j*4 + 12);
 

and see what happens when we make some of the integer constants into floating-point constants of the same value.

Again, keep an eye on the type of each expression.

// Integer and floating-point division 
#include<stdio.h>
int main() {
  int j = 5, k = 3;
  double x, y;

// Do division as integers:
  x = (j + 7 ) / (k*6 -j*4 + 12);

// Do division as floating-point: 
// Converting one int to floating-point is enough
  y = (j + 7.0 ) / (k*6 -j*4 + 12);

// But converting them all is safer
  y = (j + 7.0 ) / (k*6.0 -j*4.0 + 12.0);

  printf("x: %g y: %g\n", x, y);

  return 0;
}
Step through this code

 

Converting between integers and floating point

Automatic (implicit) conversion

As seen above, when evaluating expressions with two different arithmetic types C just "does the right thing" and converts things for us. For example, in the expression:

  j * x;

Since j is an integer and x a double the value of j is automatically converted to a double before the multiplication.(The variable j itself is unchanged of course.)

Integer to floating-point conversions and assignments are also common and again they "just work":

  j = 12;
  x = j;

just sets x to 12.

Implicit floating-point to integer conversion

Setting an integer variable to the value of a floating-point expression rounds to zero, just like integer division:

  x = 1.82;
  j = x;   // j has the value 1

Explicit conversion between types

We have already seen how we can force division involving constant integer values to be treated as floating point by just adding ".0". But what if we wish to divide two integer variables to obtain a floating-point result? We can't add ".0" to a variable name!

Given the previous discussion on integer division we can see that the following code which tries to calculate an average is incorrect:

  int number, total;
  double average;

  ...

  average = total/number;           /* Wrong! */

as total and number are both integers and that therefore the expression total/number is also an integer.

The way to divide two integers in a floating-point way is to explicitly convert one or both of the integers to a double. Any integer expression can be converted to a double expression of the same value by the simple, if slightly unintuitive, method of writing "(double) expression" :

We can put any valid type inside the parentheses to convert an expression to a different type.

  int number, total;
  double average;

  ... 

  average = total/((double) number);          

Thus the expression (double) number means "the value of number treated as a double, not an integer". If number had the value 7 then (double) number is treated it as if it were 7.0 . Since we are now dividing an integer by a double, the integer expression on the top is also converted to a double and all is well. This is referred to as casting the original expression to the new type.

The extra parentheses on the bottom are optional, we could have simply written:
  average = total/(double) number;          

Expressions of the form (typename) sub-expression
explicitly convert the sub-expression to that type.

Casts bind extremely tightly so (double) total/number is equivalent to ((double) total)/number which is nearly always what we want.

Complex numbers

Complex numbers weren't available in early versions of C as they are highly specialised and only used by mathematicians, scientists and engineers.

The include file complex.h allows us to conveniently use complex numbers using the double complex type with I (note it's capital!) being the square root of minus one.

#include <complex.h> lets us use complex numbers and functions.

Complex versions of most of the math.h functions are also inside complex.h, all with a c in front of the name such as csqrt(), csin(), casin(), cexp(), cpow(),  etc. The functions creal(), cimag() give the real and imaginary parts respectively with conj(), cabs() and carg() also available. For obvious mathematical reasons these last three functos return a double (ie a real number)..

cabs() is the complex, absolute value, fabs() is the real floating-point absolute value and abs() is the integer absolute value.

Once we've done that complex arithmetic goes just as we would expect, although we have to split expressions into their real and imaginary parts for printing:

Internaly C implements complex numbers as a pair of float-point numbers stored one after the other in memory, although we don't need to know that in order to use them.

//
// Demonstrate complex arithmetic and the complex square root.
//
#include <stdio.h>
#include <complex.h>

int main() {
  double complex x, y, sqrtx;

  x = 1.2 + 3.4 * I;

  y = -13.7 * x + 5.6 * x * x - 12.7 * I;
  sqrtx = csqrt(x);

  printf("x is  %g %+g * I\n", creal(x), cimag(x));
  printf("y is  %g %+g * I\n", creal(y), cimag(y));
  printf("sqrt(x) is %g %+g * I\n", creal(sqrtx), cimag(sqrtx));

  return 0;
}
Step through this code


The output is:

The format %+g in printf()means "always print a plus if the number is positive".

x is  1.2 +3.4 * I
y is  -73.112 -13.584 * I
sqrt(x) is 1.55009 +1.09671 * I

Observe that we could have written the last printf() function as

  printf("sqrt(x) is %g %+g * I\n", creal(csqrt(x)), cimag(csqrt(x)));

as the arguments to functions are always the value of expressions, we never "pass a variable" to a function in C.

  1. Complex arithmetic
  2. To practice complex arithmetic and functions
  3. Create a new on-line program in a new window, with a suitable title and opening comment.
  4. #include the file complex.h
  5. Declare three double complex variables and give two of them complex values. (See the above example for how to do this.)
  6. Make the third equal to the product of the first two.
  7. Print out the complex square-root of all three numbers using %g (three times of course).
    • Do the numbers look reasonable?

Reading variables from the keyboard with scanf()

We have seen how to write the values of mathematical expressions to the screen:

      int ivar = 1;
      float fvar = 2.2;
      double dvar = 3.3;
      
      printf("%d %g %g\n", ivar, fvar, dvar);

The above example writes an int, a float (single-precision floating-point number) and a double (double-precision floating-point number) to the screen. Conveniently we may use "%g" for both the single and precision floating-point numbers.

It's so useful to be able to read in numbers from the keyboard that we show how the very basics here, although the full details will have to wait until a later lecture.

We will be covering scanf() more thoroughly in a later lecture, this is just a very quick recipe.

Here we read in the values of the three variables from the keyboard and you will immediately see a few differences:

For more details read the lecture on Memory, input and ouput

      scanf("%d %g %lg", &ivar, &fvar, &dvar);

Notes: 

We use scanf() instead of printf()

We have to put an ampersand & in front of the variable names in calls to scanf().

The format to read a double is %lg (The "l" stands for long.) but to write a double is %g.

(We shall explain why in a later lecture but for the time being we will just have to remember.)

There is no \n at the end of the format string for scanf(), or anything else inside the format string except spaces

For example, no commas between the formats. Commas and new-lines are allowed but they do not mean what you think they mean.

#include <stdio.h>
int main() {
      int ivar = 1;
      float fvar = 2.2;
      double dvar = 3.3;
      
      printf("%d %g %g\n", ivar, fvar, dvar);

      printf("Please enter an integer and two floating-point numbers\n");
      scanf("%d %g %lg", &ivar, &fvar, &dvar);

      printf("%d %g %g\n", ivar, fvar, dvar);

      return 0;
}
Step through this code


Although we shall sometimes refer to scanf() in our notes and non-marked exercises we will always tell you how to use scanf() in our assessed work until it has been covered in more depth in a later lecture.

  1. Reading in variables
  2. To practice using scanf()
  3. If you have trouble with this exercise leave it until the lab class.
  4. Take your "people and cars" mini-exercise and modify it to read in the numbers of people and cars.
  5. Do the same with your momentum code. Reminder: use %lg to read in a double.

Summary

The text of each key point is a link to the place in the web page.

Arithmetic expressions

Standard mathematical functions

Integer variables and expressions

Complex numbers

Reading variables from the keyboard with scanf()

Log in
                                                                                                                                                                                                                                                                       

Validate   Link-check © Copyright & disclaimer Privacy & cookies Share
Back to top