Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

MATLAB & GNU Octave  Module 2 – Vectors, matrices and numeric types

Numeric types: double, single, integers, complex

See why 0.1 + 0.2 is not 0.3 in Octave and MATLAB, predict integer rounding and saturation, use complex numbers and both transposes, and convert types.

  • Intermediate
  • 20 minutes
  • Examples run with octave 11.3.0 and Python 3.14.8
  • By MySmartCoPilot
Free lesson, Pro answers: 20 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Explain double precision, eps and why 0.1 + 0.2 is not 0.3, and compare results with a tolerance
  • Use integer classes safely by predicting their rounding, saturation and mixed-class rules
  • Work with complex numbers and choose between the two transpose operators
  • Convert between types with cast, typecast and hexadecimal and binary literals

Before you start

What you’ll learn · Before you start

What you will learn

  • Explain double precision, eps and why 0.1 + 0.2 is not 0.3, and compare results with a tolerance
  • Use integer classes safely by predicting their rounding, saturation and mixed-class rules
  • Work with complex numbers and choose between the two transpose operators
  • Convert between types with cast, typecast and hexadecimal and binary literals

Before you start

On this page

Add 0.1 ten times in Octave and the total is 0.99999999999999989, so s == 1 is false; subtract 5 from the 8-bit unsigned integer 3 and you get 0. Neither is a bug: each numeric class stores numbers its own way, and its rules decide every result you compute with it.

Here you see how doubles store fractions and how far apart they are, compare results with a tolerance, predict what integer classes do when a result is a fraction or out of range, mix classes, use complex numbers and the two transposes, and convert between types without losing data by accident.

Numbers are doubles, and a double is a binary fraction

Every number you type is a double unless you ask for another class: 8 bytes in the 64-bit format of the IEEE 754 standard, which MATLAB and Octave both use. A double is a sum of powers of two, and no finite sum of powers of two equals one tenth, in the same way that no finite decimal equals one third. Octave therefore keeps the double closest to 0.1, about 5.6e-18 too large, as the first line of the output shows, and such tiny errors add up over several operations. This program shows it, then compares two ways of testing results:

float_traps.m: equality tests on doubles, and tolerances M · float_traps.m
% float_traps.m: equality tests that fail on doubles, and comparisons that work
printf('0.1 is stored as %.25f\n', 0.1);
printf('0.1 + 0.2 = %.17g, and 0.1 + 0.2 == 0.3 is %d\n', 0.1 + 0.2, 0.1 + 0.2 == 0.3);

s = 0;                          % add 0.1 ten times
for k = 1:10
  s = s + 0.1;
  printf('after %2d additions: %.17g\n', k, s);
end
printf('s == 1 is %d; abs(s - 1) < 1e-12 is %d\n', s == 1, abs(s - 1) < 1e-12);

% an absolute tolerance has no right size: the same slip is larger on larger numbers
a = 1e6 * (0.1 + 0.2);
b = 1e6 * 0.3;
printf('a - b = %.3g; abs(a - b) < 1e-12 is %d\n', a - b, abs(a - b) < 1e-12);

% a relative tolerance scales with the numbers; NaN is never close to anything
isclose = @(x, y, rtol, atol) abs(x - y) <= max(atol, rtol * max(abs(x), abs(y)));
printf('isclose(a, b, 1e-12, 0) is %d\n', isclose(a, b, 1e-12, 0));
printf('isclose(NaN, NaN, 1e-12, 0) is %d\n', isclose(NaN, NaN, 1e-12, 0));
float_traps.m, then eps and flintmax at the prompt, GNU Octave 11.3.0 Python · float_session.py
from session import prompt, run, version

version()
run("octave-cli --no-init-all float_traps.m")
prompt("eps_demo.m")

Output

# GNU Octave (aarch64-apple-darwin25.4.0) version 11.3.0
$ octave-cli --no-init-all float_traps.m
0.1 is stored as 0.1000000000000000055511151
0.1 + 0.2 = 0.30000000000000004, and 0.1 + 0.2 == 0.3 is 0
after  1 additions: 0.10000000000000001
after  2 additions: 0.20000000000000001
after  3 additions: 0.30000000000000004
after  4 additions: 0.40000000000000002
after  5 additions: 0.5
after  6 additions: 0.59999999999999998
after  7 additions: 0.69999999999999996
after  8 additions: 0.79999999999999993
after  9 additions: 0.89999999999999991
after 10 additions: 0.99999999999999989
s == 1 is 0; abs(s - 1) < 1e-12 is 1
a - b = 5.82e-11; abs(a - b) < 1e-12 is 0
isclose(a, b, 1e-12, 0) is 1
isclose(NaN, NaN, 1e-12, 0) is 0
$ octave-cli --quiet
octave:1> printf('%.17g\n', eps(1), eps(1000), eps(1e6))
2.2204460492503131e-16
1.1368683772161603e-13
1.1641532182693481e-10
octave:2> eps(2^53)
ans = 2
octave:3> flintmax
ans = 9.0072e+15
octave:4> flintmax + 1 == flintmax
ans = 1
octave:5> eps('single')
ans = 1.1921e-07
octave:6> flintmax('single')
ans = 1.6777e+07
octave:7> single(16777216) + 1 == single(16777216)
ans = 1

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 float_session.py

The ten additions drift: after 3 the sum is 0.30000000000000004, after 6 it is 0.59999999999999998, and after 10 it is 0.99999999999999989, so s == 1 is 0, false. Comparing with a tolerance, abs(s - 1) < 1e-12, accepts it.

A fixed tolerance has no right size, though. a = 1e6 * (0.1 + 0.2) and b = 1e6 * 0.3 differ by 5.82e-11, larger than 1e-12, because the same relative slip is a million times larger on numbers a million times larger. A relative tolerance, as in the program’s isclose, scales with the numbers, and it says 1. It also says 0 for two NaN, which is right: NaN is not close to anything, itself included.

Testing computed doubles with ==

if 0.1 + 0.2 == 0.3 is never true. Compare computed values with a tolerance that fits their size, such as abs(x - y) <= 1e-12 * max(abs(x), abs(y)), and keep == for values that are exact, such as counts and whole numbers below 2^53.

Checkpoint

Check: Doubles and comparing with a tolerance

  1. Question 1 of 2 s starts at 0 and 0.1 is added ten times. What does s == 1 give?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: 0, false, because s is 0.99999999999999989

    float_traps.m printed the sum after each addition: 0.99999999999999989 after ten, so == says false and a tolerance is needed.

  2. Question 2 of 2 a = 1e6 * (0.1 + 0.2) and b = 1e6 * 0.3 differ by 5.82e-11. Which test calls them equal without accepting real differences in other data?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: abs(a - b) <= 1e-12 * max(abs(a), abs(b))

    The slip of a computed value is about eps times its size, so the tolerance must scale too: the program's isclose(a, b, 1e-12, 0) printed 1.

eps is the gap to the next double, and it grows with the number

Doubles are spaced unevenly: there are as many between 1 and 2 as between 1,000 and 2,000. eps(x) gives the gap from x to the next larger double, which machine epsilon: The gap between 1 and the next larger double, eps(1) = 2.2204e-16 in MATLAB and Octave. eps(x) gives the gap next to any x, which grows with the size of x., eps(1), sets for numbers near 1. The prompt part of the session above measured it:

Number Gap to the next double, eps(x)
1 2.2204460492503131e-16
1,000 1.1368683772161603e-13
1,000,000 1.1641532182693481e-10
2^53 = 9,007,199,254,740,992 2

The gap is between about 1.1e-16 and 2.2e-16 times the number, which is why a relative tolerance works and an absolute one does not. At 2^53 the gap reaches 2: flintmax, the largest whole number below which every whole number is exact, is 9,007,199,254,740,992, and flintmax + 1 == flintmax is 1, true, because the sum rounds back. Above it only every second whole number is a double, so counters and IDs there skip values or collide.

single is the 32-bit format, 4 bytes: about 7 significant digits, eps('single') 1.1921e-07, and exact whole numbers only up to flintmax('single'), 16,777,216, where adding 1 changed nothing.

Key idea

A double carries about 16 significant digits and its gap grows with its size, so compare computed values with a relative tolerance, and keep exact comparisons for whole numbers below 2^53.

Checkpoint

Check: eps, flintmax and single precision

  1. Question 1 of 2 eps(1) is 2.2204460492503131e-16. About how large is eps(1000)?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: About 1.1e-13, 512 times larger

    eps(1000) is 1.1368683772161603e-13, about 512 times eps(1): 1000 lies between 2^9 and 2^10, and the gaps double at each power of two.

  2. Question 2 of 2 What does flintmax + 1 == flintmax give, and why?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: 1, true, because at 2^53 the gap between doubles is 2 and the sum rounds back

    flintmax is 2^53, the largest whole number below which every whole number is exact. One more cannot be stored, so the sum rounds back and == is true.

Integer classes round and saturate instead of wrapping

The integer classes int8, uint8, int16, uint16, int32, uint32, int64 and uint64 hold whole numbers in 1, 2, 4 or 8 bytes. Every result is first worked out exactly, then rounded to a whole number, halves away from zero, then set to the nearest end of the class’s range when it falls outside, which is called saturation: Setting an integer result that falls outside its class's range to the nearest end of the range instead of wrapping round, so that int8(127) + 1 is 127 and uint8(3) - 5 is 0.. Step through five results from the sessions:

Interactive · stage by stage How Octave turns an integer calculation into a result Step through five lines of the sessions below and watch which stage changes each answer.
How Octave turns an integer calculation into a result, step 6 of 6. Showing The exact result (1 + 2.7 = 3.7), Round to a whole number (3.7 becomes 4), Clamp to the class range (4 is in range), Store in the integer class (int8: 4). In focus: The exact result, Round to a whole number, Store in the integer class.The exact result1 + 2.7 = 3.7Round to a whole number3.7 becomes 4Clamp to the class range4 is in rangeStore in the integer classint8: 4

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. int8(127) + 1. The exact sum is 128. It is already a whole number, so rounding changes nothing.
  2. Clamped, not wrapped. An int8 holds -128 to 127, so 128 is set to the largest value, 127. Many other languages would wrap round to -128 instead.
  3. uint8(3) - 5. The exact result is -2, but a uint8 holds 0 to 255, so it is clamped to 0. Unsigned classes turn every negative result into 0.
  4. int32(7) / int32(2). Integer division is rounded like everything else. The exact quotient is 3.5, and a half rounds away from zero, so the answer is 4.
  5. int32(-7) / int32(2). The exact quotient is -3.5, and away from zero means down here, to -4. idivide is documented to round towards zero instead, though Octave 11.3.0's idivide rounds negative quotients down, as the warning below the session shows.
  6. int8(1) + 2.7. Mixed with a double, the integer class wins. The exact sum 3.7 rounds to 4, and the result is an int8, as class(m) reported.

Integer rounding and saturation, GNU Octave 11.3.0 Python · int_session.py
from session import prompt, version

version()
prompt("int_rules.m")

Output

# GNU Octave (aarch64-apple-darwin25.4.0) version 11.3.0
$ octave-cli --quiet
octave:1> int8(127) + 1
ans = 127
octave:2> int8(-128) - 1
ans = -128
octave:3> uint8(3) - 5
ans = 0
octave:4> int8(2.5)
ans = 3
octave:5> int8(-2.5)
ans = -3
octave:6> int32(7) / int32(2)
ans = 4
octave:7> int32(-7) / int32(2)
ans = -4
octave:8> idivide(int32(7), int32(2))
ans = 3
octave:9> idivide(int16([-7 -4 7 11]), int16(10))
ans =

  -1  -1   0   1

octave:10> int16(fix(double(int16([-7 -4 7 11])) / 10))
ans =

  0  0  0  1

octave:11> [intmin('int8'), intmax('int8')]
ans =

  -128  127

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 int_session.py

int8(127) + 1 is 127 and int8(-128) - 1 is -128: the result stays at the edge of the range instead of wrapping round to the other end, as integers do in many other languages. uint8(3) - 5 is 0. int8(2.5) is 3 and int8(-2.5) is -3. Division rounds like everything else, so int32(7) / int32(2) is 4 and int32(-7) / int32(2) is -4, while idivide lets you choose the rounding and is documented to round towards zero by default: idivide(int32(7), int32(2)) is 3.

idivide rounds negative quotients down in Octave 11.3.0

The default rounding of idivide is "fix", towards zero, in both programs’ documentation, and MathWorks shows idivide(int16([-7 -4 7 11]), int16(10)) giving 0 0 0 1. Octave 11.3.0 returned -1 -1 0 1, rounding the two negative quotients down. Where signs can be negative, compute in double and convert: int16(fix(double(a) / double(b))) gave 0 0 0 1.

Checkpoint

Check: Integer rounding and saturation

  1. Question 1 of 2 What does this line show?

    Read the code, then choose one answer.

    uint8(250) + 10
    Show the answer: Question 1 of 2

    Answer: ans = 255

    Integer results saturate at the ends of the range: 260 becomes 255, as int8(127) + 1 became 127.

  2. Question 2 of 2 You need integer division that rounds towards zero for counts that may be negative, and the same results in MATLAB and Octave 11.3. Which line does that?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: int32(fix(double(a) ./ double(b)))

    idivide(int16([-7 -4 7 11]), int16(10)) gave -1 -1 0 1 in Octave 11.3.0, where MathWorks shows 0 0 0 1. Working in double with fix, then converting, gave 0 0 0 1 in the session.

Mixed classes follow fixed rules, and integers win

When a calculation mixes classes, the result’s class follows rules that MATLAB and Octave share, and most of them surprise people at first:

adc_counts.m: 12-bit ADC counts to volts M · adc_counts.m
% adc_counts.m: 12-bit ADC counts to volts around a mid-scale offset
counts = uint16([2048 3000 1000 4095 0]);   % raw readings, 0 to 4095
offset = 2048;                              % the count at 0 V
scale = 3.3 / 4096;                         % volts per count

wrong = (counts - offset) * scale;          % computed in uint16
printf('wrong (%s): %s\n', class(wrong), mat2str(wrong));

right = (double(counts) - offset) * scale;  % computed in double
printf('right (%s): %s\n', class(right), mat2str(right, 4));
Mixed classes, then adc_counts.m, GNU Octave 11.3.0 Python · mixed_session.py
from session import prompt, run, version

version()
prompt("mixed_rules.m", errors=1)
run("octave-cli --no-init-all adc_counts.m")

Output

# GNU Octave (aarch64-apple-darwin25.4.0) version 11.3.0
$ octave-cli --quiet
octave:1> m = int8(1) + 2.7
m = 4
octave:2> class(m)
ans = int8
octave:3> int8([1 2]) + [1.5 2.5]
ans =

  3  5

octave:4> int8(1) + int16(1)
error: binary operator '+' not implemented for 'int8 scalar' by 'int16 scalar' operations
octave:5> class(single(1) + 1)
ans = single
octave:6> t = true + true
t = 2
octave:7> class(t)
ans = double
$ octave-cli --no-init-all adc_counts.m
wrong (uint16): [0 1 0 2 0]
right (double): [0 0.767 -0.8443 1.649 -1.65]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 mixed_session.py

Mix Result class In the session
integer and double the integer class int8(1) + 2.7 is the int8 4
two different integer classes an error int8(1) + int16(1)
single and double single class(single(1) + 1)
logical and logical double true + true is the double 2

MATLAB adds one condition to the first row: a double that meets an integer array must be a scalar, as MathWorks’ page on floating-point numbers states. Octave 11.3 accepts a double row as well: int8([1 2]) + [1.5 2.5] gave the int8 values 3 and 5. Code for both programs therefore keeps such doubles scalar, or converts first.

The first row causes the classic bug, and adc_counts.m has it. A 12-bit converter reads 0 to 4095, stored as uint16, with 2048 meaning 0 V. (counts - offset) * scale is worked out in uint16: every reading below 2048 is clamped to 0, and the volts are rounded to whole numbers, [0 1 0 2 0]. Converting first, double(counts) - offset, gives the right volts, [0 0.767 -0.8443 1.649 -1.65].

Which class to store data in depends on what you will do with it. Answer three questions:

Interactive · answer and compare Which class should hold your measurements? Answer three questions about your data. Every class is shown as it is until you answer, then the table marks what fits and why.
Do the values or the results have fractions, such as volts or metres?
Must hundreds of millions of values sit in memory at once?
Will you subtract offsets, scale, divide or average the values?

Answer the questions above: the table then marks what fits your case and says why the rest does not.

double, single, int16 and uint8 against four things measurement data needs
Class Memory per value Fractions Exact whole numbers Safe arithmetic
double Holds: 64-bit floating point, the default Poor 8 bytes: 800 MB for 100 million values Good about 16 digits; eps(1) is 2.2e-16 Good every whole number up to flintmax, 9,007,199,254,740,992 Good nothing is clamped or rounded to whole numbers
single Holds: 32-bit floating point Mixed 4 bytes: 400 MB for 100 million values Mixed about 7 digits; eps('single') is 1.19e-07 Poor exact only up to 16,777,216; adding 1 there changed nothing Mixed single plus double gives single, with single precision
int16 Holds: whole numbers from -32,768 to 32,767 Good 2 bytes: 200 MB for 100 million values Poor every result is rounded to a whole number Mixed exact from -32,768 to 32,767, clamped beyond Poor division rounds and overflow clamps, without a warning
uint8 Holds: whole numbers from 0 to 255 Good 1 byte: 100 MB for 100 million values Poor every result is rounded to a whole number Poor only 0 to 255 Poor uint8(3) - 5 gave 0: any negative result becomes 0
How the answers decide
  • “Do the values or the results have fractions, such as volts or metres?” Yes, fractions matter: rules out int16 and uint8. Integer classes round every result to a whole number, so the volts of adc_counts.m came out as 0 1 0 2 0.
  • “Will you subtract offsets, scale, divide or average the values?” Yes, they go into calculations: adds a caveat to int16, uint8 and single. Store compactly if you must, but convert to double for the arithmetic, as double(counts) - offset did; integers clamp and round, single keeps 7 digits.
  • “Must hundreds of millions of values sit in memory at once?” Yes, memory is tight: adds a caveat to double. 8 bytes a value is 800 MB per 100 million. Keep raw data in a smaller class and convert blocks to double for the calculations.
  • “Must hundreds of millions of values sit in memory at once?” No, memory is not a concern and “Will you subtract offsets, scale, divide or average the values?” Yes, they go into calculations: adds a caveat to int16, uint8 and single. Without a memory limit, double is the simple choice for anything that is calculated with.

Store compactly, calculate in double

Raw counts from sensors and images are often kept as integers to save memory. Convert with double at the start of any calculation, and only convert back when you store the result.

Checkpoint

Check: Mixed classes

  1. Question 1 of 2 What class is int8(1) + 2.7?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: int8, holding 4

    m = int8(1) + 2.7 gave m = 4 and class(m) gave int8.

  2. Question 2 of 2 counts is uint16 and offset = 2048. Why does (counts - offset) * 3.3 / 4096 give [0 1 0 2 0], and what is the fix?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The arithmetic stays in uint16, so negatives clamp to 0 and volts round to whole numbers; write double(counts) - offset

    adc_counts.m printed wrong (uint16): [0 1 0 2 0] and right (double): [0 0.767 -0.8443 1.649 -1.65]. Convert before the first subtraction.

Complex numbers need no setup, but one transpose conjugates them

Complex numbers need no special setup: 3 + 4i is one, and functions that leave the real numbers, such as sqrt(-1), return one. The session also shows the two transposes on a row with a complex element:

Complex numbers and transposes, GNU Octave 11.3.0 Python · complex_session.py
from session import prompt, version

version()
prompt("complex_demo.m")

Output

# GNU Octave (aarch64-apple-darwin25.4.0) version 11.3.0
$ octave-cli --quiet
octave:1> z = 3 + 4i;
octave:2> [abs(z), real(z), imag(z)]
ans =

   5   3   4

octave:3> w = [1+2i, 3];
octave:4> w'
ans =

   1 - 2i
   3 - 0i

octave:5> w.'
ans =

   1 + 2i
   3 + 0i

octave:6> sqrt(-1)
ans =  0 + 1i
octave:7> log(0)
ans = -Inf
octave:8> (-8)^(1/3)
ans =  1.0000 + 1.7321i
octave:9> nthroot(-8, 3)
ans = -2

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 complex_session.py

abs(3 + 4i) is 5, the length, and real and imag take the parts. The important line is the transpose: for w = [1+2i, 3], w' turned 1 + 2i into 1 - 2i, because ' is the conjugate transpose: The transpose that also changes the sign of every imaginary part, written ' in MATLAB and Octave. The plain transpose, written .', only swaps rows and columns; for real data they agree., while w.' only rearranged the elements. For real data the two give the same result, which is why the difference goes unnoticed until complex values arrive, for example from an fft.

Two more lines are worth a look. log(0) is -Inf, not an error. And (-8)^(1/3) is 1.0000 + 1.7321i, the principal complex cube root, while nthroot(-8, 3) is the real root, -2.

Using ' to turn a row of complex values into a column

w' conjugates every element as it transposes. To rearrange data without changing it, write w.'. Keep ' for the linear algebra that needs the conjugate, such as the length sqrt(w * w') of a complex vector.

Checkpoint

Check: Complex numbers and the two transposes

  1. Question 1 of 2 What is the first element of the result?

    Read the code, then choose one answer.

    w = [1+2i, 3];
    w'
    Show the answer: Question 1 of 2

    Answer: 1 - 2i

    w' gave the column 1 - 2i, 3 - 0i, and w.' the column 1 + 2i, 3 + 0i.

  2. Question 2 of 2 What does (-8)^(1/3) return in Octave 11.3?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: 1.0000 + 1.7321i, a complex root

    For the real cube root of a negative number, use nthroot(-8, 3), which gave -2.

cast, typecast and literals convert on purpose

Converting a value to another class is a choice about what to keep. cast(x, 'uint8') and uint8(x) keep the number, rounded and saturated as above. typecast keeps the bits and reads them as another class, and integer literals choose a class as you type them:

Conversions and integer literals, GNU Octave 11.3.0 Python · convert_session.py
from session import prompt, version

version()
prompt("convert_demo.m")

Output

# GNU Octave (aarch64-apple-darwin25.4.0) version 11.3.0
$ octave-cli --quiet
octave:1> cast(300, 'uint8')
ans = 255
octave:2> double(int8(-5)) / 2
ans = -2.5000
octave:3> typecast(single(1), 'uint32')
ans = 1065353216
octave:4> x = 0x1F
x = 31
octave:5> class(x)
ans = uint8
octave:6> b = 0b1010
b = 10
octave:7> v = 0x1FFu16;
octave:8> class(v)
ans = uint16
octave:9> n = 0xF0s8
n = -16

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 convert_session.py

  • cast(300, 'uint8') is 255: the number is kept as well as the class allows. double(int8(-5)) / 2 is -2.5, because the division happens in double.
  • typecast(single(1), 'uint32') is 1065353216: the same 32 bits read as an unsigned integer. It is the tool for reading raw bytes from a file or a device, and wrong for changing a number’s class.
  • Hexadecimal and binary literals such as 0x1F and 0b1010 are uint8 in Octave 11.3, the smallest unsigned class that holds them, and a suffix picks another: 0x1FFu16 is a uint16 and 0xF0s8 is the int8 -16, read in two’s complement. MathWorks documents the same rule and suffixes for MATLAB, where these literals arrived in R2019b.

Checkpoint

Check: cast, typecast and literals

  1. Question 1 of 2 Which call turns the double 300 into a uint8 while keeping as much of the number as the class allows?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: cast(300, 'uint8'), which gives 255

    cast(x, class) and the class functions keep the value, clamped to the range: cast(300, 'uint8') was 255. typecast keeps the bits instead.

  2. Question 2 of 2 What class is the literal 0x1F in Octave 11.3?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: uint8

    class(0x1F) was uint8. A suffix such as u16 or s8 picks another class, so 0x1FFu16 is a uint16.

Practise: classes on your own data

Do these in Octave, and check each against what Octave prints:

  1. Add 0.1 a hundred times and compare the total with 10, first with ==, then with a relative tolerance.
  2. Store the counts uint16([100 2048 4000]) and convert them to volts around 2048 with the scale 3.3 / 4096, first wrongly in uint16, then correctly in double. The correct second value is 0.
  3. Predict int8(100) * 2, uint8(250) + 10, int16(-7) / int16(2) and idivide(int16(-7), int16(2)), then check. Which of them would MATLAB’s documentation give differently?
  4. Make a complex column from w = [1+2i, 3-1i] with w.', then with w', and say which line changes the signs.
IEEE 754 Floating-Point Converter Enter 0.1 and look at the bits of the double nearest to it, and at the error they carry. Number Base Converter Convert 0x1F, 0b1010 and 0xF0 to decimal and compare with the literals in the session.

The self-check below asks up to twelve questions on doubles, integers, complex numbers and conversions, with a reason for each answer:

Practice that adapts to you

Practise this lesson

13 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 13 What is 0.1 + 0.2 when printed with 17 significant digits?

    Choose one answer.

    Show the answer: Question 1 of 13

    Answer: 0.30000000000000004

    float_traps.m printed 0.30000000000000004, and 0.1 + 0.2 == 0.3 was 0.

  2. Question 2 of 13 What does a relative-tolerance test such as abs(x - y) <= 1e-12 * max(abs(x), abs(y)) give for x = NaN, y = NaN?

    Choose one answer.

    Show the answer: Question 2 of 13

    Answer: 0, false

    isclose(NaN, NaN, 1e-12, 0) printed 0. To treat two missing values as equal, test isnan on both first.

  3. Question 3 of 13 eps(1) is 2.2e-16. Which value is eps(1e6), the gap from a million to the next double?

    Choose one answer.

    Show the answer: Question 3 of 13

    Answer: 1.16e-10

    Octave printed eps(1e6) = 1.1641532182693481e-10. Doubles between 2^19 and 2^20 are 2^-33 apart.

  4. Question 4 of 13 A counter stored as single is incremented by 1 for every event. What happens after 16,777,216 events?

    Choose one answer.

    Show the answer: Question 4 of 13

    Answer: It stops growing, because single(16777216) + 1 equals single(16777216)

    single(16777216) + 1 == single(16777216) was 1. Count in double, exact to 2^53, or in an integer class.

  5. Question 5 of 13 What does the line show?

    Read the code, then choose one answer.

    int8(-2.5)
    Show the answer: Question 5 of 13

    Answer: ans = -3

    int8(2.5) was 3 and int8(-2.5) was -3: halves go away from zero.

  6. Question 6 of 13 What does the line show?

    Read the code, then choose one answer.

    int32(7) / int32(2)
    Show the answer: Question 6 of 13

    Answer: ans = 4

    int32(7) / int32(2) printed 4, and idivide(int32(7), int32(2)) printed 3.

  7. Question 7 of 13 What happens with int8(1) + int16(1)?

    Choose one answer.

    Show the answer: Question 7 of 13

    Answer: An error, because two different integer classes cannot be mixed

    Convert one of them first, for example int16(a) + b, or both to double.

  8. Question 8 of 13 What is class(true + true)?

    Choose one answer.

    Show the answer: Question 8 of 13

    Answer: double

    t = true + true gave t = 2 and class(t) gave double, which is why sum of a logical array counts its trues.

  9. Question 9 of 13 counts is a uint16 row and offsets a double row of the same length. counts - offsets runs in Octave 11.3. What should code that must also run in MATLAB do?

    Choose one answer.

    Show the answer: Question 9 of 13

    Answer: Convert first, double(counts) - offsets

    Octave 11.3 gave int8([1 2]) + [1.5 2.5] = 3 5, while MathWorks' page on floating-point numbers requires a scalar double next to an integer array. double(counts) - offsets runs in both and computes in double.

  10. Question 10 of 13 What is abs(3 + 4i)?

    Type a number.

    Show the answer: Question 10 of 13

    Answer: 5

    abs of a complex number is its length, sqrt(3^2 + 4^2) = 5, as the session printed.

  11. Question 11 of 13 A row of complex FFT values must become a column, unchanged, before it is written to a file. Which operator do you use?

    Choose one answer.

    Show the answer: Question 11 of 13

    Answer: .' , the plain transpose

    w.' gave 1 + 2i, 3 + 0i, while w' gave 1 - 2i, 3 - 0i. For data, use .' ; for linear algebra that needs the conjugate, '.

  12. Question 12 of 13 Why is typecast(single(1), 'uint32') 1065353216 rather than 1?

    Choose one answer.

    Show the answer: Question 12 of 13

    Answer: typecast keeps the 32 bits of single(1) and reads them as an unsigned integer

    cast and the class functions convert values; typecast reinterprets bits, the right tool only for raw bytes.

  13. Question 13 of 13 What does the line show?

    Read the code, then choose one answer.

    n = 0xF0s8
    Show the answer: Question 13 of 13

    Answer: n = -16

    0xF0s8 printed n = -16. Without a suffix, 0xF0 would be the uint8 240.

Interview questions

Warm-up: why is 0.1 + 0.2 == 0.3 false in MATLAB and Octave, and how should you compare such values?

MATLAB and Octave store numbers as IEEE 754 doubles, sums of powers of two, and none of 0.1, 0.2 or 0.3 is such a sum, so each is held as the closest double instead. The rounding errors of 0.1 and 0.2 add up to 0.30000000000000004, which is one double away from the stored 0.3, so == says false. This is how the arithmetic is defined in every language that uses IEEE doubles, not a fault of MATLAB or Octave. Compare computed values with a tolerance instead: an absolute one, abs(x - y) < 1e-12, for numbers near 1, and a relative one, abs(x - y) <= 1e-12 * max(abs(x), abs(y)), for anything else, because the gap between doubles grows with their size, from 2.2e-16 at 1 to 1.2e-10 at a million.

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Fresher to mid

    A 12-bit ADC delivers counts from 0 to 4095 as uint16, with 2048 meaning 0 V. The code computes volts = (counts - 2048) * 3.3 / 4096, and every negative voltage comes out as 0 and every positive one as a whole number. Diagnose it, fix it, and say how you would test the fix.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    A detector produces 100 million 16-bit samples per run. You store them, compute a calibrated signal and report its mean and its total energy. Which class do you use to store, to compute and to sum, and what goes wrong with each wrong choice?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Numbers are IEEE 754 doubles by default, binary fractions in which 0.1 is not exact, so sums drift and computed values are compared with a tolerance, not with ==.
  • eps(x) is the gap to the next double and grows with x, so tolerances are relative; whole numbers are exact up to flintmax, 2^53, and single has about 7 digits and stops at 16,777,216.
  • Integer classes round to the nearest whole number, halves away from zero, and saturate at intmin and intmax instead of wrapping; integer division rounds too.
  • An integer mixed with a double gives the integer class, two integer classes cannot mix, and single wins over double, so convert counts with double before calculating.
  • ' is the conjugate transpose and .' the plain one; complex results such as (-8)^(1/3) appear without a warning.
  • cast keeps the number, typecast keeps the bits, and 0x and 0b literals pick the smallest unsigned class unless a suffix chooses another.

Terms in this lesson

conjugate transpose
The transpose that also changes the sign of every imaginary part, written ' in MATLAB and Octave. The plain transpose, written .', only swaps rows and columns; for real data they agree.
machine epsilon
The gap between 1 and the next larger double, eps(1) = 2.2204e-16 in MATLAB and Octave. eps(x) gives the gap next to any x, which grows with the size of x.
saturation
Setting an integer result that falls outside its class's range to the nearest end of the range instead of wrapping round, so that int8(127) + 1 is 127 and uint8(3) - 5 is 0.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.