[英]How does MATLAB's colon operator work?
As noted in this answer by Sam Roberts and this other answer by gnovice , MATLAB's colon operator ( start:step:stop
) creates a vector of values in a different way that linspace
does. 正如Sam Roberts的 回答和gnovice的其他答案所述 ,MATLAB的冒号运算符( start:step:stop
)以与linspace
不同的方式创建值向量。 In particular, Sam Roberts states: Sam Roberts特别指出:
The colon operator adds increments to the starting point, and subtracts decrements from the end point to reach a middle point. 冒号运算符将增量添加到起始点,并从结束点减去减量以达到中间点。 In this way, it ensures that the output vector is as symmetric as possible. 这样,它确保输出向量尽可能对称。
However, offical documentation about this from The MathWorks has been deleted from their site. 但是,有关The MathWorks的官方文档已从其网站中删除。
If Sam's description is correct, wouldn't the errors in the step sizes be symmetric? 如果Sam的描述是正确的,步长中的错误不会是对称的吗?
>> step = 1/3;
>> C = 0:step:5;
>> diff(C) - step
ans =
1.0e-15 *
Columns 1 through 10
0 0 0.0555 -0.0555 -0.0555 0.1665 -0.2776 0.6106 -0.2776 0.1665
Columns 11 through 15
0.1665 -0.2776 -0.2776 0.6106 -0.2776
Interesting things to note about the colon operator: 关于冒号运算符的有趣事项:
Its values depend on its length: 它的值取决于它的长度:
>> step = 1/3; >> C = 0:step:5; >> X = 0:step:3; >> C(1:10) - X ans = 1.0e-15 * 0 0 0 0 0 -0.2220 0 -0.4441 0.4441 0
It can generate repeated values if they are rounded: 如果它们被舍入,它可以生成重复值:
>> E = 1-eps : eps/4 : 1+eps; >> E-1 ans = 1.0e-15 * -0.2220 -0.2220 -0.1110 0 0 0 0 0.2220 0.2220
There is a tolerance for the last value, if the step size creates a value just above the end, this end value is still used: 最后一个值有一个容差,如果步长在末尾创建一个值,则仍然使用此结束值:
>> A = 0 : step : 5-2*eps(5) A = Columns 1 through 10 0 0.3333 0.6667 1.0000 1.3333 1.6667 2.0000 2.3333 2.6667 3.0000 Columns 11 through 16 3.3333 3.6667 4.0000 4.3333 4.6667 5.0000 >> A(end) == 5 - 2*eps(5) ans = logical 1 >> step*15 - 5 ans = 0
The deleted page referred to by Sam's answer is still archived by the Way Back Machine . Sam回答中提到的删除页面仍由Way Back Machine归档 。 Luckily, even the attached M-file colonop
is there too. 幸运的是,即使附加的M文件colonop
也存在。 And it seems that this function still matches what MATLAB does (I'm on R2017a): 似乎这个函数仍然匹配MATLAB的功能(我在R2017a上):
>> all(0:step:5 == colonop(0,step,5))
ans =
logical
1
>> all(-pi:pi/21:pi == colonop(-pi,pi/21,pi))
ans =
logical
1
I'll replicate here what the function does for the general case (there are some shortcuts for generating integer vectors and handling special cases). 我将在这里复制函数对一般情况的作用(有一些生成整数向量和处理特殊情况的快捷方式)。 I'm replacing the function's variable names with more meaningful ones. 我正在用更有意义的变量替换函数的变量名。 The inputs are start
, step
and stop
. 输入是start
, step
和stop
。
First it computes how many steps there are in between start
and stop
. 首先,它计算start
和stop
之间的步数。 If the last step exceeds stop
by more than a tolerance, it is not taken: 如果最后一步超过stop
超过公差,则不会采取:
n = round((stop-start)/step);
tol = 2.0*eps*max(abs(start),abs(stop));
sig = sign(step);
if sig*(start+n*step - stop) > tol
n = n - 1;
end
This explains the last observation mentioned in the question. 这解释了问题中提到的最后一个观察结果。
Next, it computes the value of the last element, and makes sure that it does not exceed the stop
value, even if it allowed to go past it in the previous computation. 接下来,它计算最后一个元素的值,并确保它不超过stop
值,即使它允许在先前的计算中超过它。
last = start + n*step;
if sig*(last-stop) > -tol
last = stop;
end
This is why the lasat value in the vector A
in the question actually has the stop
value as the last value. 这就是为什么问题中向量A
中的lasat值实际上具有stop
值作为最后一个值的原因。
Next, it computes the output array in two parts, as advertised: the left and right halves of the array are filled independently: 接下来,它按两个部分计算输出数组,如公布的那样:数组的左半部分和右半部分是独立填充的:
out = zeros(1,n+1);
k = 0:floor(n/2);
out(1+k) = start + k*step;
out(n+1-k) = last - k*step;
Note that they are not filled by incrementing, but by computing an integer array and multiplying it by the step size, just like linspace
does. 请注意,它们不是通过递增来填充,而是通过计算整数数组并将其乘以步长来填充,就像linspace
一样。 This exaplains the observation about array E
in the question. 这使得在问题中关于阵列E
的观察成为可能。 The difference is that the right half of the array is filled by subtracting those values from the last
value. 不同之处在于通过从last
值中减去这些值来填充数组的右半部分。
As a final step, for odd-sized arrays, the middle value is computed separately to ensure it lies exactly half-way the two end points: 作为最后一步,对于奇数大小的数组,中间值是单独计算的,以确保它恰好位于两个端点的中间位置:
if mod(n,2) == 0
out(n/2+1) = (start+last)/2;
end
The full function colonop
is copied at the bottom. 完整函数colonop
复制在底部。
Note that filling the left and right side of the array separately does not mean that the errors in step sizes should be perfectly symmetric. 请注意,分别填充数组的左侧和右侧并不意味着步长中的错误应该是完全对称的。 These errors are given by roundoff errors. 这些错误由舍入误差给出。 But it does make a difference where the stop
point is not reached exactly by the step size, as in the case of array A
in the question. 但是,如果没有完全按步长来达到stop
点,那么确实会产生差异,就像问题中数组A
的情况一样。 In this case, the slightly shorter step size is taken in the middle of the array, rather than at the end: 在这种情况下,稍微缩短的步长是在数组的中间,而不是在结尾:
>> step=1/3;
>> A = 0 : step : 5-2*eps(5);
>> A/step-(0:15)
ans =
1.0e-14 *
Columns 1 through 10
0 0 0 0 0 0 0 -0.0888 -0.4441 -0.5329
Columns 11 through 16
-0.3553 -0.3553 -0.5329 -0.5329 -0.3553 -0.5329
But even in the case where the stop
point is reached exactly, some additional error accumulates in the middle. 但即使在精确到达stop
点的情况下,也会在中间累积一些额外的误差。 Take for example the array C
in the question. 以问题中的数组C
为例。 This error accumulation does not happen with linspace
: linspace
不会发生此错误累积:
C = 0:1/3:5;
lims = eps(C);
subplot(2,1,1)
plot(diff(C)-1/3,'o-')
hold on
plot(lims,'k:')
plot(-lims,'k:')
plot([1,15],[0,0],'k:')
ylabel('error')
title('0:1/3:5')
L = linspace(0,5,16);
subplot(2,1,2)
plot(diff(L)-1/3,'x-')
hold on
plot(lims,'k:')
plot(-lims,'k:')
plot([1,15],[0,0],'k:')
title('linspace(0,5,16)')
ylabel('error')
colonop
: colonop
:
function out = colonop(start,step,stop)
% COLONOP Demonstrate how the built-in a:d:b is constructed.
%
% v = colonop(a,b) constructs v = a:1:b.
% v = colonop(a,d,b) constructs v = a:d:b.
%
% v = a:d:b is not constructed using repeated addition. If the
% textual representation of d in the source code cannot be
% exactly represented in binary floating point, then repeated
% addition will appear to have accumlated roundoff error. In
% some cases, d may be so small that the floating point number
% nearest a+d is actually a. Here are two imporant examples.
%
% v = 1-eps : eps/4 : 1+eps is the nine floating point numbers
% closest to v = 1 + (-4:1:4)*eps/4. Since the spacing of the
% floating point numbers between 1-eps and 1 is eps/2 and the
% spacing between 1 and 1+eps is eps,
% v = [1-eps 1-eps 1-eps/2 1 1 1 1 1+eps 1+eps].
%
% Even though 0.01 is not exactly represented in binary,
% v = -1 : 0.01 : 1 consists of 201 floating points numbers
% centered symmetrically about zero.
%
% Ideally, in exact arithmetic, for b > a and d > 0,
% v = a:d:b should be the vector of length n+1 generated by
% v = a + (0:n)*d where n = floor((b-a)/d).
% In floating point arithmetic, the delicate computatations
% are the value of n, the value of the right hand end point,
% c = a+n*d, and symmetry about the mid-point.
if nargin < 3
stop = step;
step = 1;
end
tol = 2.0*eps*max(abs(start),abs(stop));
sig = sign(step);
% Exceptional cases.
if ~isfinite(start) || ~isfinite(step) || ~isfinite(stop)
out = NaN;
return
elseif step == 0 || start < stop && step < 0 || stop < start && step > 0
% Result is empty.
out = zeros(1,0);
return
end
% n = number of intervals = length(v) - 1.
if start == floor(start) && step == 1
% Consecutive integers.
n = floor(stop) - start;
elseif start == floor(start) && step == floor(step)
% Integers with spacing > 1.
q = floor(start/step);
r = start - q*step;
n = floor((stop-r)/step) - q;
else
% General case.
n = round((stop-start)/step);
if sig*(start+n*step - stop) > tol
n = n - 1;
end
end
% last = right hand end point.
last = start + n*step;
if sig*(last-stop) > -tol
last = stop;
end
% out should be symmetric about the mid-point.
out = zeros(1,n+1);
k = 0:floor(n/2);
out(1+k) = start + k*step;
out(n+1-k) = last - k*step;
if mod(n,2) == 0
out(n/2+1) = (start+last)/2;
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.