4.7 Arithmetic
Of course, fsc2
understands the usual arithmetic operations,
i.e. addition, subtraction, multiplication and division, represented
by the characters `+
', `-
', `*
' and `/
'.
They can be used with simple integer and floating point values as well
as with 1-dimensional arrays (see below for more information). If one of
the values is a floating point value the result is also a floating point
number - only if both values are integers the result is also an
integer. This also holds for the division - if you divide two integers
the result is still an integer, see below.
Some care has to be taken in arithmetic with integers. The range of values an integer variable can have is restricted to the interval [-2147483648, +2147483647], i.e. [-2^31, 2^31 - 1] (at least on a 32-bit processor). Thus, it isn't to difficult to create numbers with greater values, e.g. by multiplication, that can't be represented by an integer! In this case the result of the operation will be completely bogus, e.g.
1000000 * 3000 = -1294967296 |
(There is actually some logic behind this result but that doesn't help
too much.) So, if you suspect the result of an integer operation to
exceed the range of representable values, convert the values to
floating point type (using the function float()
) before
you do the potentially problematic arithmetic operation.
Of course, there are also limits to the range of numbers representable by a floating point number. Fortunately, these limits are much larger - typically the maximum size of floating point numbers is in the range of 10^300.
Another important point about calculations involving integers concerns the division. The result of the division of two integer values is again an integer value. For non-integer results this is achieved by simply truncating the digits following the decimal point. Thus, typical results are
7 / 2 = 3 8 / 3 = 2 -7 / 2 = -3 |
To avoid this truncation convert at least one of the values used in
the division to a floating point type by using the
float()
function, i.e.
float( 7 ) / 2 = 3.5 8 / float( 3 ) = 2.666666667 |
Beside the normal arithmetic operations there are two often needed
operations, the modulo function and the exponentiation. For the modulo
operation the percent sign %
is used and the result is the remainder of the division, e.g.
7 % 3 = 1 6.5 % 2.5 = 1.5 |
For the exponentiation the caret character ^
is to be used, e.g.
3^2 = 9 6.5^1.5 = 16.5718134219... |
As mathematics dictate non-integer exponents can only be used with non-negative bases!
• Arithmetic-assignment operators | ||
• Operator precedence | ||
• Arithmetic with arrays and matrices |
4.7.1 Arithmetic-assignment operators
When assigning to a variable usually the assignment operator `=
'
is used. But there are often cases where a variable just has to be
incremented as in
variable:
I = I + 1; |
This assignment can be shortened (and sometimes made more readable) by
using the `add and assign' operator `+=
':
I += 1; |
Assignment operators mixed with arithmetic can also be used with all other arithmetic operators, i.e. you may use all of the following assignment operators
+= -= *= /= %= ^= |
4.7.2 Operator precedence
Usually one doesn't need to think about the precedence of operators. We already learned in primary school that multiplication and division have higher precedence than addition and subtraction and, of course, a programming language should follow these rules. But sometimes there are cases where it is necessary to know the precedence exactly, i.e. the exact sequence an expression is evaluated.
The unary operators (unary in the sense that they apply only to one
number or variable) `+
' and `-
', i.e. the operators
defining the sign of a number, have the highest precedence. There is a
further operator, to be discussed later, having the same precedence, the
logical negation operator, `!
'. The evaluation of an expression
with more than one of these operators is from the right to the left,
i.e.
!-x will be treated as if written as ! ( - x )
|
(This is not a very useful example but here for sake of completeness.)
The exponentiation operator, `^
', has the next highest
precedence, i.e. it binds more tightly than any other binary operator
(i.e. an operator involving two numbers or variables). If there is
more than one of these operators in a row the expression will be
evaluated from the right to the left:
2^3^4 will be treated as if written as 2^(3^4)
|
The operator with the next lower precedence is the modulo operator,
`%
'. Expressions with more than one `%
' in a row will be
evaluated left to right, i.e.
31 % 6 % 2 will be treated as if written as ( 31 % 6 ) % 2
|
Now follow the multiplication and division operators, `*
' and
`/
. Both have exactly the same precedence and expressions
involving more than one of these operators are evaluated left to right,
i.e.
3 * 20 / 7 will be treated as if written as ( 3 * 20 ) / 7
|
(Take care: The order of evaluation can make a lot of a difference - due
to the rules of integer division (3 * 20) / 7
will evaluate to
8
, while 3 * (20 / 7)
would result in 6
).
The next lower precedence operators are the addition and subtraction
operators, `+
' and `-
'. Also for these operators
expressions involving more than one of these operators are evaluated
left to right:
13 + 4 - 5 will be treated as if written as ( 13 + 4 ) - 5
|
Usually, this won't matter a lot, but if you're dealing with very large numbers and there's danger of an overflow to occur knowing the rules of evaluation can become important.
The next lower precedence operators are the logical operators to be
discussed in more detail below, i.e. the logical and operator,
`AND
' or `&
', the logical or operator, `OR
' or
`|
' and the logical exclusive or (xor) operator, `XOR
'
or `~
'. Expressions containing more than one of these operators
are always evaluated from the left to the right of the expression:
a OR b AND c will be treated as if written as ( a OR b ) AND c
|
or
a | b & c will be treated as if written as ( a | b ) & c
|
To make your intentions more clear to human readers of your
EDL
scripts it's probably a good idea to use parenthesis in
these cases even if they are not strictly required.
The operators with the second-lowest precedence are the comparison operators
(also the be discussed below in more detail), i.e. the test for equality,
`==
' (not to be confused with the assignment operator
`=
'), the test for inequality, `!=
', and the remaining
four comparison operators `<
', `<=
', `>
' and
`>=
', i.e. less than, less or equal, larger and
larger or equal. If more than one of these operators appears in an
expression evaluation is done left to right.
Finally, the lowest precedence has the conditional operator, which has the form
expression_1 ? expression_2 : expression_3 |
It tests expression_1
and then results in expression_2
being evaluated if the result of the test was non-zero, or in
expression_3
being calculated if the test failed (i.e.
resulted in a zero value), a more detailed explanation is given below.
Actually, there are also the assignment operators. But these are only used after all of the other operators have been evaluated, so there is never a necessity to enclose the right hand of an equation in parenthesis. And since only one assignment operator is allowed in a statement you don't have to worry about the order of evaluation.
Of course, the precedence of operators and the sequence they are evaluated in can always be changed by using parentheses. So, if in doubt, use parentheses - this won't slow down the program but will often make the script easier to understand.
4.7.3 Arithmetic with arrays and matrices
Beside the usual arithmetic with numbers it is also possible to use
whole arrays and more-dimensional matrices in arithmetic expressions.
Lets start with 1-dimensional arrays. As long as the sizes of the two
arrays are identical they can be added or subtracted as if they were
numbers. For example, if the arrays a
and b
are defined
as
a[ 3 ] = { 0.5, 1.0, 2.0 }; b[ 3 ] = { 1.0, -2.0, -3.0 }; |
they can be added and subtracted in an element-by-element fashion, resulting in
a + b -> { 1.5, -1.0, -1.0 } a - b -> { -0.5, 3.0, 5.0 } |
Of course, you can also invert the sign of all the elements of an array by simply prepending it with a minus sign
-a -> { -0.5, -1.0, -2.0 }; |
But it's also possible to add a number to each of the elements of an array or to subtract a number (or to subtract all array elements from a number):
a + 5 -> { 5.5, 6.0, 7.0 } a - 3 -> { -2.5, -2.0, -1.0 } 3 - a -> { 2.5, 2.0, 1.0 } |
Multiplication and division are also possible with whole arrays. Again,
these operations are implemented in an element-by-element way, i.e.
multiplying two arrays is not a dot product but results again in an
array of the same size as both the original arrays. The same holds for
the division. With the arrays a
and b
defined above the
results are:
a * b -> { 0.5, -2.0, -6.0 } a / b -> { 0.5, -0.5, -0.666667 } b / a -> { 2.0, -2.0, -1.5 } |
Multiplication and division of an array with a number is also defined as shown here:
a * 2 -> { 1.0, 2.0, 4.0 } a / 3 -> { 0.166667, 0.333333, 0.666667 } 3 / a -> { 6.0, 3.0, 1.5 } |
Finally, calculation of the modulo function and exponentiation can be done with whole arrays. These operations are again implemented as element-by-element calculations, i.e. always resulting in an array:
a % b -> { 0.5, 1.0, 2.0 } b % a -> { 0.0, 0.0, -1.0 } a ^ b -> { 0.5, 1.0, 0.125 } b ^ a -> { 1.0, -2.0, 9.0 } |
Of course, the same operations can also be applied to a mixture of arrays and simple numbers, also resulting in arrays:
a % 0.3 -> { 0.2, 0.1, 0.2 } 1.3 % a -> { 0.3, 0.3, 0.7 } a ^ 2 -> { 0.25, 1.0, 4.0 } 2 ^ a -> { 1.414215, 2.0, 4.0 } |
All these kinds of arithmetics also work with more-dimensional arrays. If the size of two matrices are identical they can be added together or subtracted from each other, then elements can be multiplicated or divided in an element by element fashion (take care, multiplication of two 2-dimensional matrices does don't work in the usual mathematical way, but the first elements of the first rows of both matrices get multiplicated, forming the first element of the first row of the resulting matrix). And also the modulo and exponentiation operations work as well as the negation.
And as for 1-dimensional arrays a single number can be e.g. added to
each of the elements of a more-dimensional array. I.e. if M
is
a 3-dimensional matrix you can add the number 3 to each of it's elements
(as far as they exist yet in the case of variable sized matrices) by
simply writing
M += 3; |
Of course, this also works with all the other kinds of arithmetic
operations. The only requirement is, if M
is a variable sized
matrix, all elements exist, i.e. if for example a sub-array isn't yet
defined fsc2
will flag an error because it doesn't know how to
add a number to an array that not exists yet.
If you want to add a number to only a sub-array of the matrix
M[ 2 ] += 3; |
would add 3 to each elements of the 2nd sub-array of M
(which is
a matrix of rank 4x5).
But you can also add a whole 1-dimensional array to each of the
sub-arrays of a matrix. Lets again assume that M
3-dimensional,
e.g. a matrix of rank 3x4x5. If a
is now an 1-dimensional
array of length 5 you can add it to all of M
's 12
sub-sub-arrays by writing
M += a; |
If you would have to write the same using just the elements you would have to write 60 additions instead, i.e.
M[ 1, 1, 1 ] += a[ 1 ]; M[ 1, 1, 2 ] += a[ 2 ]; ... M[ 1, 1, 5 ] += a[ 5 ]; M[ 1, 2, 1 ] += a[ 1 ]; ... ... M[ 3, 4, 5 ] += a[ 5 ]; |
Of course, this could also be done using a FOR
loop, see below,
but would still be a lot longer.
If you instead want to add the array a
only to all the sub-array
in the 2nd set of sub-arrays of M
you could simply write
M[ 2 ] += a; |
instead of doing it explicitely in the longer form
M[ 2, 1 ] += a; M[ 2, 2 ] += a; M[ 2, 3 ] += a; M[ 2, 4 ] += a; |
You can also directly add a 2-dimensional matrix to all sub-matrices of
a 3-dimensional matrix, or, generally, you can add a n-dimensional
matrix to all n-dimensional sub-matrices of a m-dimensional matrix,
given that m is larger than n and that the ranks of the sub-matrices are
identical, by just writing them as if they were simple numbers. The same
holds for all the other types of arithmetic operations. In all cases
fsc2
will automatically try to get it right and spare you from
having to write all kinds of loops to get the same effect.
You can also mix objects of different ranks in an expression: if
M
is, for example, a 3-dimensional matrix, N
is a
2-dimensional matrix and R
an 1-dimensional array you can write
M += N - R * 3; |
In this case each element of R
will first be multiplied by 3 and
the resulting array is then subtracted from each of the sub-arrays of
N
. Finally, N
is added to each of the sub-matrices of
M
.
Further, the built-in arithmetic functions can (as far as this makes
any sense) be applied to arrays and matrices. In every case the
function is applied to each element of the array or matrix, thus the
result is another array or matrix of the same rank with its elements
being the results of applying the function to each of the input array
elements. For example, again using the array a
defined above,
applying the sqrt()
function results in:
sqrt( a ) -> { 0.7071068, 1.0, 1.1414215 } |
Finally, for obvious reasons, when computed assignments are used
(i.e. `+=
', `-=
', `*=
', `/=
', `%=
'
or `^=
') the left hand side variable of an equation has to be an
array when arithmetic with arrays is used.
This document was generated by Jens Thoms Toerring on September 6, 2017 using texi2html 1.82.