How does sub2ind work with non-integer values?

I was working with some displacement matrices and noticed sub2ind working with non-integer inputs. It not only works but also outputs non-integer values. I looked at the documentation but didn't see an explanation for this behavior.
For example:
sub2ind([2 2],1,1.8)
ans = 2.6000
So, if I round the output to 3, is it going to be the linear index that is closest to (1,1.8) based on Euclidean distance? What is the intended behavior here?
Thanks in advance and apologies if I missed this in the documentation.

12 Comments

"How does sub2ind work with non-integer values?"
Open the M file and take a look, its algorithm is very simple.
"What is the intended behavior here?"
Most likely that users do not supply fractional values.
I wish I could have upvoted the @Stephen23 comment, because fractional values are not expected, and that sub2ind was never intended to be used in that way. I suppose they might have put in some additional error checks to flag such an issue, but at some point, additional error checks just suck away additional CPU cycles. And if you are using sub2ind, I have a funny feeling that those additonal unwarranted error checks will be almost always unappreciated.
"How does sub2ind work with non-integer values?"
"Open the M file and take a look, its algorithm is very simple."
I guess I should have asked why because I was more surprised that it worked at all. I shouldn't have to dig in the codebase to understand the authors' intentions though. Nevertheless, I did, and the algorithm might be simple but it's not particularly legible:
Stephen23: Copyright code deleted.
Also, there are already a lot of checks here for the inputs as there should be for a function that handles fundamental things like indices. There is a check to make sure the input is real. They could have checked to make sure it's an integer instead. This could be an unintended behavior that is hiding in your codebase right now giving wrong results that you are not aware of.
"They could have checked to make sure it's an integer instead."
Integers can be negative, which does not really help things either.
"This could be an unintended behavior that is hiding in your codebase right now giving wrong results that you are not aware of."
Make a bug report (or enhancement request, depending on how you look at it):
Matt J
Matt J on 22 Jul 2025
Edited: Matt J on 22 Jul 2025
Open the M file and take a look, its algorithm is very simple.
I'll just take this opportunity once again to say how amazing it is that sub2ind and ind2sub still haven't been reimplemented as optimized builtins, considering how frequently they are used, and how important they are to fast performance. Certainly if speed is the reason a check for integer-ness was omitted, it seems all the more sensible to abandon the Mcoded version.
Is there a use case for sub2ind where its output is not used for a subsequent indexing operation?
I don't know but it can be problematic even if you use it exclusively for indexing. When you try to index with fractionals you get this error:
"Array indices must be positive integers or logical values."
Rounding the output of sub2ind fixes the error but gives wrong results:
img(round(sub2ind(size(img),Ys,Xs)))
You need to round the coordinates to get the correct output:
img(sub2ind(size(img),round(Ys),round(Xs)))
The first solution looks just as valid unless you are paranoid or until you notice all your results are wrong somewhere down the line.
You need to round the coordinates to get the correct output:
There are no "correct results" for using sub2ind with non-integers. It is inherently a wrong thing to do.
I wonder if the developers assumed that the output of sub2ind would be used only for indexing and were therefore relying on the non-integer case to throw an error downstream when used in an actual indexing operation.
I can't see how any application other than indexing would be envisioned by the developers. The "ind" in sub2ind stands for indexing.
It's also worth noting that ind2sub does not invert sub2ind when non-integer arguments are permitted
k=sub2ind([2 2],1,1.8)
k = 2.6000
[i,j]=ind2sub([2 2],k)
i = 2.6000
j = 1
Hi Matt, after you commented on noninversion I added some material on the topic (more or less) in my answer below.
"I wonder if the developers assumed that the output of sub2ind would be used only for indexing and were therefore relying on the non-integer case to throw an error downstream when used in an actual indexing operation."
If that were the case, I guess it would have been a mistake insofar as a valid output can be obtained from an invalid input
sub2ind([100,100],61,10)
ans = 961
sub2ind([100,100],11,10.5)
ans = 961

Sign in to comment.

Answers (1)

Hi Saygin,
Interesting behavior. The help for sub2ind says it 'returns the linear index equivalent' and I guess they really mean it about the linear part. For 2d, If the input matrix size is mxn and the input indices are p,q then the result is
sub2ind([m,n],p,q) = p + (q-1)*m
so long as 1<=p<=m and 1<=q<=n, even if none of them (including m and n) are integers. Evidently Matlab only checks the inequalities just listed, not for being integers.
---------
Matt's comment 'ind2sub does not invert sub2ind when non-integer arguments are permitted' brought up the idea of what kind of process could be inverted.
Suppose the matrix is size mxn, and r and c are row and column indices. ind2sub allows any of those four quantities to be noninteger. Let j be the index,
j = sub2ind([m n],r,c)
and let k be the index you would get if you read the matrix out rowwise rather than columnwise. Or if you prefer, by reading out the transposed version in the usual manner,
k = sub2ind([n m],c,r)
Let 'bar' quantities be one less than actual quantites; rbar = r-1 etc. Turn r and c into a 2x1 vector, same with j and k. Then there is a nice relationship among the barred quantities
m = 3.4; n = 2.7; r = 3.1; c = 1.8;
j = sub2ind([m n],r,c)
s2i = [1 m;n 1];
rcbar = [r;c]-1;
jkbar = s2i*rcbar; % fwd
jk = jkbar+1 % jk(1) = j
rcbar = s2i\jkbar; % inv
rc = rcbar+1 % agrees
j = 5.8200
jk = 5.8200
7.4700
rc = 3.1000
1.8000

Products

Release

R2024a

Tags

Asked:

on 21 Jul 2025

Edited:

on 25 Jul 2025

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!