Skip to main content
Engineering LibreTexts

5.1: Single-Index Floating Point Arrays

  • Page ID
    55651
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Concept

    It is often the case that we have an ordered set of, say \(n\), "elements" of data which are somehow related. The index could represent directions in three space dimensions \((k=1,2,3\) for \(x, y, z\), respectively) - at which we store, in each array location, the corresponding coordinate of a point (an array of length 3); or the index could represent 15 different times - at which we store, in each location, the time of the measurement, or perhaps the measurement itself (an array of length 15). Note the index plays the role of independent variable and the array value plays the role of dependent variable. In all these cases we will often wish to operate on all \(n\) "related elements" in a similar fashion. We would thus like a way to reference all the elements with a common name, and an easy way to reference different elements through this common name, in order to develop succinct, readable, and efficient code for implementing common operations on all the elements. In particular, we would not want to write \(n\) lines of code each time we wished, say, to square each of the \(n\) elements.

    A single-index array - in this section, a floating point single-index array - is the simplest "class" which achieves these objectives. We first introduce a variable name, array_name, which shall be associated to all \(n\) elements of the array. We then index this array_name in order to access any particular element of the array: in particular, array_name (i) is the pointer to element \(i\) of the array. We emphasize that, as in the scalar case, array_name (i) is not the value of element i of the array but rather the location in memory at which we shall store the value of element \(i\). For example, array_name \((2)=3.14159\) would assign the value \(3.14159\) to the second element of the array. (We discuss more efficient assignment methods below.)

    Conceptually, you may think of the array as stored at \(n\) contiguous locations in memory. Element 1 of the array is stored in array_name (1), element 2 of the array is stored in array_name (2), \(\ldots\), and element \(n\) of the array is stored in array_name \((\mathrm{n})\). In this (virtual) sense, it suffices to (say) pass to a function simply the variable array name - which you may view as the address of the first element - as the addresses of all the other elements of the array can then be readily deduced from array name. (In actual practice, there is also some header information associated with the array - for example, in our single-index case, the length \(n\).) Since many common array operations can be performed in MATLAB with simple function calls - or user-defined function calls - at a high-level we can often deal exclusively with array_name without explicit reference to particular indices. (Of course, under the hood...)

    Assignment and Access

    The most explicit way to create a single-index array is by hand: \(X=[1,3,4]\) creates a singleindex array of length 3 , with entries \(X(1)=1, X(2)=3\), and \(X(3)=4\). To determine the length of an array MATLAB provides a function length. In our example

    >> X = [1,3,4]
    X =
        1     3     4
    >> X(1) 
        ans = 1
    >> length(X)
    ans =
        3
    >>
    

    Note that this single-index array is a row single-index array. (We can also consider column singleindex arrays, in which the commas above are replaced by semi-colons: \(\mathrm{X}=[1 ; 3 ; 4]\). We reserve treatment of rows and columns to our discussion of double-index arrays.)

    The input process is facilitated by the colon operator. In particular, \(\mathrm{Z}=[\mathrm{J}: \mathrm{D}: \mathrm{K}]\) creates the single-index array \(J, J+D, \ldots, J+m * D\) ] for \(m=f i x((K-J) / D)\), where \(f i x\) is a MATLAB function which rounds to the nearest integer towards zero. (Note that \(\mathrm{J}: \mathrm{D}: \mathrm{K}\) is empty if \(\mathrm{D}==0\), if \(D>0 \& J>K\), or if \(D<0\) & \(J<K\).) The default value of \(D\) is 1 and hence \(J: K\) is equivalent to \(\mathrm{J}: 1: \mathrm{K}\), as in the example

    >> firstfive = [1:5]
    firstfive =
        1     2     3     4     5
    >>
    

    You will in fact recognize this colon construction from the for statement; and indeed the for statement may take the form for VARCOUNTER \(=\mathrm{S}\) where \(\mathrm{S}\) is any single-index array; we will revisit this construction of the for statement shortly.

    We may assign an entire array,

    >> Y = X
    Y =    
        1     3     4
    >>
    

    or we may assign or re-assign a particular element as, say,

    >> X(3) = 10;
    >> X
    X =
        1     3     10
    >>
    

    which we see modifies \(X\) accordingly.

    Of course the point of an array is that we have a systematic numerical approach to indexing, and we may thus easily assign values with a for statement. We present two approaches. In the first, we zero out to initialize:

    >> Z = zeros(1,10);
    >> for i = 1:length(Z)
        Z(i) = i^2;
       end
    >> Z
    Z =
        1     4     9     16     25     36     49     64     81     100
    >>
    

    Note that zeros \((1, n)\) is a MATLAB function which provides a (row) single-index array of all zeros of length \(n\). (Note the first argument of zeros indicates that we wish to form a row single-index array; zeros \((n, 1)\) would create a column single-index array of all zeros. We will understand zeros better once we discuss multi-index arrays.) This approach, of initialization, is preferred whenever possible: it permits MATLAB to be more efficient in memory allocation and management.

    In the second approach, we concatenate:

    >> Z = [];
    >> for i = 1:10
        Z = [Z,i^2];
       end
    >> Z  
    Z =
        1     4     9     16     25     36     49     64     81     100
    >>
    

    Here \(Z=[]\) defines an array but the array is initially empty: we do this because we can not "add to" (append to, or concatenate to) an array which does not exist. Note that the expression [Z, \(\left.i^{\wedge} 2\right]\) evaluates to an array in which the first length ( \(Z\) ) elements are the elements of \(Z\) and the last element is \(i^{\wedge} 2\). Thus at each iteration the length of \(\mathrm{Z}\) grows. The above is less efficient than the initialization approach, but very convenient in particular (for example, in a while statement) when we do not a priori know the number of elements in our (ultimate) array.

    As our last point on assignment and access, we note that MATLAB supports a very convenient form of indirect addressing. In particular, if we create a single-index array of integers indvec then we can extract from (say) Z just those elements with indices in indvec:

    >> indvec = [1,3,5,9];
    >> U = Z(indvec)
    U =
        1     9     25     81
    >>
    

    Note you may also apply indirect addressing on the left-hand side of the assignment statement, but some care must be exercised as regards the “shape” (row vs. column) of the resulting single-index array.

    Note that in all these shortcuts there is always an equivalent underlying program (which is more or less how these shortcuts are implemented). For example, in the above, an array index argument tells Matlab to execute, effectively:

    >> U_too = zeros(1,length(indvec))
    >> for inew = 1:length(indvec)
            U_too(inew) = Z(indvec(inew));
       end
    >> U_too
    U_too =
        1     9     25     81
    >>
    

    But of course much better to encapsulate and re-use this feature within the Matlab syntax than to re-write this little loop on each occasion.

    (Dotted) Arithmetic Operations

    It is one of the major advantages of programming languages that as we develop more convenient data structures (or types, or classes), in particular with many elements, we may also suitably define our operations to deal directly with these new entities — as a whole, rather than explicitly manipulating each element. In this section we define for the case of single-index arrays the corresponding array arithmetic operators.

    We first discuss element-by-element operations. In these operations, we consider two arrays of the same length and we apply the same arithmetic operation on each pair of elements which share the same index. We begin with addition/subtraction (and multiplication by a scalar):

    >> P = [1, 4, 7]; Q = [2, -1, 1];
    >> R = P + 3.0*Q
    R =
        7     1     10
    >>
    

    which is simply shorthand for the loop

    >> for i = 1:length(P)
            R_too(i) = P(i) + 3.0*Q(i);
       end
    >> R_too
    R_too =
        7     1     10
    >>
    

    The loop makes clear the interpretation of “element by element,” but obviously the one-line statement is much preferred.

    Note \(3.0\) is not an array, it is scalar, which scales all elements of the array Q in our above example. This simple scaling feature can be very convenient in defining arrays of grid points or data points (for example, in interpolation, differentiation, and integration). To wit, if we have an interval of length \(L\) and wish to create \(N\) segments of length \(L / N\), we need only do \(>>\) xpts \(=(\mathrm{L} / \mathrm{N}) *[0: \mathrm{N}]\). Note that xpts is of length (in the sense of number of elements in the array) \(\mathrm{N}+1\) since include both endpoints: \(\operatorname{xpts}(1)=0\) and \(\operatorname{xpts}(\mathrm{N}+1)=\mathrm{L} / \mathrm{N}\).

    Finally, we note one additional shortcut: if \(q\) is a scalar, then element-by-element addition to our vector \(P\) will add \(q\) to each element of \(P\). To wit,

    >> q = 3;
    >> P + q
    ans =
        75     4     7     10
    >>
    

    We might write the above more properly as

    >> P + q*ones(1,length(P))
    ans =
        4     7     10
    >>
    

    but there is no need given MATLAB’s automatic expansion of q when encountered in array addition. Note ones \((1, n)\) is a MATLAB function which creates a (row) single-index array of length \(n\) with all elements set to unity.

    To implement element-by-element multiplication, division, and exponentiation we do

    >> PdotmultQ = P.*Q
    PdotmultQ =
        2     -4     7
    >> PdotdivideQ = P./Q
    PdotdivideQ =
        0.5000     -4.0000     7.0000
    >> PdotexpQ = P.^Q
    PdotexpQ =
        1.0000     0.2500     7.0000
    >>
    

    which is equivalent to

    >> for i = 1:length(P)
        PdotmultQ_too(i) = P(i)*Q(i);
        PdotdivideQ_too(i) = P(i)/Q(i);
        PdotexpQ_too(i) = P(i)^Q(i);
       end
    >> PdotmultQ_too
    PdotmultQ_too =
        2     -4     7
    >> PdotdivideQ_too
    PdotdivideQ_too =
        0.5000     -4.0000     7.0000
    >> PdotexpQ_too
    PdotexpQ_too =
        1.0000     0.2500     7.0000
    >>
    

    As for addition, if we replace one of our vectors by a scalar, Matlab will expand out with a “ones” vector.

    Why do we need the "dot" before the \(*, /\), and ^ operators - so-called "dotted" (or element-byelement) operators: dotted multiplication, dotted division, and dotted exponentiation? It turns out that there are two types of entities which look very similar, respectively arrays and vectors (later multi-index arrays and matrices): both are ordered sets of \(n\) floating point numbers. However, the arithmetic operations are defined very differently for these two entities, respectively element-byelement operations for arrays and linear algebraic operations (e.g., inner products) for vectors. We could easily define say \(*\) to perform element-by-element multiplication for objects which are defined as arrays, and to perform an inner product for objects defined as vectors. Unfortunately, although conceptually quite clean, this would be very cumbersome since often in one line we wish to treat a set of numbers as an array and in the next line we wish to treat the same set of numbers as a vector: there would be much conversion and bookkeeping. Hence MATLAB prefers a kind of "superclass" of array-and-vector (and matrix) entities, and hence perforce some new syntax to indicate whether we wish to treat the array-and-vector as an array (with dotted element-by-element operators) or a vector (with undotted linear algebra operators). Note that we do not require dotted \(+\) (or dotted -) since element-by-element addition and vector addition in fact are equivalent. So there.

    We note that there are many arithmetic operations on arrays in addition to element-by-element operations, many of which are available as Matlab functions. For example, we can easily perform the sum of the first three integers (as we did earlier with a for loop) as

    >> ints = [1:3];
    >> sum(ints)
    ans =
        6
    >> mean(ints)
    ans =
        2
    >>
    

    where sum performs the sum of all the elements in an array (here ints) and mean calculates the arithmetic mean of all the element in an array.

    Finally, we note here that the many Matlab bui b9">sin and cos, but there are many many more — look for “math function” and “special functions” in the doc — which also accept single-index (and in fact, double–index) arguments. For example,

    >> xpts = (pi/4)*0.5*[0:2];
    >> sin_values = sin(xpts)
    sin_values =
        0     0.3827     0.7071
    >>
    

    with a single call provides the values of sin for all elements of xpts.

    Relational and Logical (Array) Operations

    For relational and logical operations we do not have the complication of array/vector conflation and hence we need no dots. In effect, when we apply any of our scalar relational/ logical operations to pairs of vectors of the same length, Matlab returns a vector (of the same length as the operands) which is the result of the scalar relational/logical operation element-by-element.

    As an example,

    >> x = [1.2, 3.3, 2.2]; y = [-0.1, 15.6, 2.0];
    >> z_1 = (x < y)
    z_1 =
        0     1     0
    >> z_2 = (x > y)
    z_2 =
        1     0     1
    >> z_3 = z_1 | ~z_2
    z_3 =
        0     1     0 
    >>
    

    Similar example can be constructed for all of our relational operators and logical operators. Note that \(z_{-} 1, z_{-} 2\) and \(z_{-} 3\) are logical arrays: each element is an instance of the logical data type.

    For completeness, we indicate the implementation of the above as a for loop:

    >> for i = 1:length(x)
        z_1_too(i) = (x(i) < y(i));
        z_2_too(i) = (x(i) > y(i));
        z_3_too(i) = z_1_too(i) | ~ z_2_too(i) ;
       end
    >> z_1_too
    z_1_too =
        0     1     0
    >> z_2_too
    z_2_too =
        1     0     1
    >> z_3_too
    z_3_too =
        0     1     0
    >>
    

    which is indeed equivalent, but tedious.

    “Data” Operations

    There are also a number of operations which albeit numerical are focused as much on the indices as the data — and hence we include these under the heading “data” operations.

    A number of Matlab functions are available to reorder the elements or to identify distinguished elements: sort, min, and max are perhaps the most useful in this regard. We illustrate just one of these functions, min:

    >> T = [4.5, -2.2, 6.3, 4.4];
    >> [minimum, minimizer] = min(T)
    minimum =
        -2.2000  
    minimizer =
        2
    >> minimum_too = min(T)
    minimum_too =
        -2.2000
    >>
    

    which yields the obvious result. This is our first example of a function with two outputs: minimum is the minimum of the array, and minimizer is the index of the minimizing element. Note also that if we just wish to obtain the first output we can abbreviate the call.

    Perhaps one of the most useful array data functions is the find function. Given a logical vector L, find(L) will return a vector which contains (in increasing order) the indices of all the elements of L which are nonzero (and are hence unity, since L is a logical array). (In fact, find can also be applied to a double array, but one must be careful about round-off effects.) As an example:

    >> L = logical([0,1,1,0,0,1]);
    >> islogical(L)
    ans =
        1
    >> ind_of_nonzero = find(L)
    ind_of_nonzero =
        2     3     6
    >>
    

    where we have also illustrated the construction of a logical vector. Note that the find function effectively implements the for loop

    >> ind_of_nonzero_too = [];
    >> for i = 1:length(L)
       if( L(i) ~= 0 )
            ind_of_nonzero_too = [ind_of_nonzero_too,i];
       end
    end
    >> ind_of_nonzero_too
    ind_of_nonzero_too =
        2     3     6      
    >>
    

    which demonstrates also an application of concatenation.

    The function find is very useful in the context of comparisons. For example, we may wish to extract just those values of vector greater than some threshold:

    >> H = [0.2, 1.4, 6.7, -3.4, 4.2];
    >> log_H_thresh = (H > 1.2)
    log_H_thresh =
        0     1     1     0     1
    >> inds_H_thresh = find(log_H_thresh)
    inds_H_thresh =
        2     3     5
    >> H(inds_H_thresh)
    ans =
        1.4000     6.7000     4.2000
    >>
    

    We can of course replace \(\mathrm{H}>1.2\) in the above with any more complicated composition of relational and logical operators.

    We could of course combine this as

    >> H ( find ( H > 1.2 ) )
    ans =
        1.4000     6.7000     4.2000
    >>
    

    In fact, Matlab accepts a further abbreviation as

    >> H ( H > 1.2 )
    ans =
        1.4000     6.7000     4.2000
    >>
    

    in which a find is automatically applied to a logical index vector. Apparently this somewhat syntactically sloppy approach is in fact the most efficient.

    We take this opportunity to revisit the for statement. We initially introduced the for statement of the form for VARCOUNTER = LIM_1:INC:LIM_2 and later noted that the for statement may take a more general form for VARCOUNTER = S where S is any single-index array. We now present an example of this general form of the for statement. Our objective is to find a number of entries in a (row) single-index array that are positive. As before, we can write the for loop based on an index as

    >> scalars = [1,-3,1,2,-5];
    >> num_pos = 0;
    >> for i = 1:length(scalars)
        if (scalars(i) > 0)
            num_pos = num_pos + 1;
        end
       end
    >> num_pos
    num_pos =
        3
    >>
    

    which also gives the correct result. In this second form, within the for loop, the loop argument sca is first set to the first element of scalars, 1, sca is then set to the second element of scalars, -3, and so on.

    We may interpret the (restrictive) form of the for statement for VARCOUNTER = LIM_1:INC :LIM_2 within this general form: LIM_1:INC:LIM_2 first yields an single-index array [LIM_1, LIM_1+INC, ..., LIM_2] (assuming LIM_2 = LIM_1 + m*INC for some integer m); then the for loop successively assigns an element of the array to VARCOUNTER. (We note that the argument of 82 the for loop must be a row single-index array, not a column single-index array. We revisit this point in our discussion of the for statement using double-index arrays.)


    This page titled 5.1: Single-Index Floating Point Arrays is shared under a CC BY-NC-SA 4.0 license and was authored, remixed, and/or curated by Masayuki Yano, James Douglass Penn, George Konidaris, & Anthony T Patera (MIT OpenCourseWare) via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.