0

I was testing a operation like this:

[input] 3.9/0.1 : 4.1/0.1
[output] 39    40

don't know why 4.1/0.1 is approximated to 40. If I add a round(), it will go as expected:

[input] 3.9/0.1 : round(4.1/0.1)
[output] 39    40   41

What's wrong with the first operation?

  • 2
    See [this Q&A explaining the colon operator](https://stackoverflow.com/q/49377234/7328782), and [this one explaining floating-point rounding errors](https://stackoverflow.com/q/686439/7328782). With those two together, you should be able to answer your question. – Cris Luengo Jul 12 '22 at 06:48

2 Answers2

2

In this Q&A I go into detail on how the colon operator works in MATLAB to create a range. But the detail that causes the issue described in this question is not covered there.

That post includes the full code for a function that imitates exactly what the colon operator does. Let's follow that code. We start with start = 3.9/0.1, which is exactly 39, and stop = 4.1/0.1, which, due to rounding errors, is just slightly smaller than 41, and step = 1 (the default if it's not given).

It starts by computing a tolerance:

tol = 2.0*eps*max(abs(start),abs(stop));

This tolerance is intended to be used so that the stop value, if within tol of an exact number of steps, is still used, if the last step would step over it. Without a tolerance, it would be really difficult to build correct sequences using floating-point end points and step sizes.

However, then we get this test:

if start == floor(start) && step == 1
   % Consecutive integers.
   n = floor(stop) - start;
elseif ...

If the start value is an exact integer, and the step size is 1, then it forces the sequence to be an integer sequence. Unfortunately, it does so by taking the number of steps as the distance between floor(stop) and start. That is, it is not using the tolerance computed earlier in determining the right stop! If stop is slightly above an integer, that integer will be in the range. If stop is slightly below an integer (as in the case of the OP), that integer will not be part of the range.

It could be debated whether MATLAB should round the stop number up in this case or not. MATLAB chose not to. All of the sequences produced by the colon operator use the start and stop values exactly as given by the user. It leaves it up to the user to ensure the bounds of the sequence are as required.

However, if the colon operator hadn't special-cased the sequence of integers, the result would have been less surprising in this case. Let's add a very small number to the start value, so it's not an integer:

>> a = 3.9/0.1 : 4.1/0.1
a =
    39    40

>> b = 3.9/0.1 + eps(39) : 4.1/0.1
b =
   39.0000   40.0000   41.0000
Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
1

Floating-point numbers suffer from loss of precision when represented with a fixed number of bits (64-bit in MATLAB by default). This is because there are infinite number of real numbers (even within a small range of say 0.0 to 0.1). On the other hand, a n-bit binary pattern can represent a finite 2^n distinct numbers. Hence, not all the real numbers can be represented. The nearest approximation will be used instead, resulted in loss of accuracy.

The closest representable value for 4.1/0.1 in the computer as a 64-bit double precision floating point number is actually,

4.1/0.1 ≈ 40.9999999999999941713291207...

So, in essence, 4.1/0.1 < 41.0 and that's what you get from the range. If you subtract, for example, 41 - 4.1/0.1 = 7.105427357601002e-15. But when you round, you get the closest value of 41.0 as expected.

The representation scheme for 64-bit double-precision according to the IEEE-754 standard:

  • The most significant bit is the sign bit (S), with 0 for positive numbers and 1 for negative numbers.
  • The following 11 bits represent exponent (E).
  • The remaining 52 bits represents fraction (F).

enter image description here

AboAmmar
  • 5,439
  • 2
  • 13
  • 24