Producing a NaN only where there is a NaN, zero otherwise

4 views (last 30 days)
This interesting question came up for me today. I was looking for a simple expression that can be used in a function handle, one that produces a NaN only for elements that are already NaN, but I want it to return 0 for any other element, including +/- inf.
X = [-inf, -1, -eps, 0, realmin, 2, 1+i, pi, flintmax, realmax, inf, NaN];
That should qualify as a good list of the possible numbers in MATLAB that might be of interest.
I want my function to generate a result as the vector: [0 0 0 0 0 0 0 0 0 0 0 NaN].
For example, this fails:
broken1 = @(X) 0*X;
broken1(X)
ans = 1×12
NaN 0 0 0 0 0 0 0 0 0 NaN NaN
Though it is close. It suffers because 0*inf generates a NaN. An as pretty one is:
broken2 = @(X) X - X;
broken2(X)
ans = 1×12
NaN 0 0 0 0 0 0 0 0 0 NaN NaN
It fails for a similar reason, because inf-inf produces NaN.
A valid solution should work for arrays of any shape or size of course. Yes, it is trivial to write if I do it in an m-file. Thus...
nanZ(X)
ans = 1×12
0 0 0 0 0 0 0 0 0 0 0 NaN
function res = nanZ(X)
% returns a NaN ONLY for elements of X that are NaN. All other elements will generate zero.
res = zeros(size(X));
res(isnan(X)) = NaN;
end
As I said, trivial if I use an m-flle. More difficult if I wish to use a function handle. It may be a blind spot on my part. But as a puzzle, can you write a simple, robust one line expression to produce my desired result, expressed as a function handle? Vectorized, of course.
And yes, I'll admit this question has essentially zero value, since a valid solution exists in an m-file form. Have fun! (I'll post a spoiler as an answer if people cannot find a better solution than the one I found. My final solution required only 9 characters, but it was definitely non-obvious.)

Answers (6)

Stephen23
Stephen23 on 22 Feb 2021
X = [-inf, -1, -eps, 0, realmin, 2, 1+i, pi, flintmax, realmax, inf, NaN];
Y = 0./(X==X)
Y = 1×12
0 0 0 0 0 0 0 0 0 0 0 NaN
  4 Comments
the cyclist
the cyclist on 22 Feb 2021
Edited: the cyclist on 22 Feb 2021
0./eq(X,X)
is a nice variant of this, but one additional character
John D'Errico
John D'Errico on 22 Feb 2021
Edited: John D'Errico on 22 Feb 2021
NIce. And it ties my solution of 9 characters. It will probably be more efficient than mine too.

Sign in to comment.


Gustavo Lunardon
Gustavo Lunardon on 24 Mar 2021
Added as an answer rather than a comment now
X = [-inf, -1, -eps, 0, realmin, 2, 1+i, pi, flintmax, realmax, inf, NaN];
broken1 = @(X) 0./X.^0;
broken1(X)
Also works, 7 characters
X = [-inf, -1, -eps, 0, realmin, 2, 1+i, pi, flintmax, realmax, inf, NaN];
broken1 = @(X) 0*X.^0;
broken1(X)
Works as well with 6 characters
Advantage is that you can put any number different than zero in the other elements by changing the multiplier
  1 Comment
the cyclist
the cyclist on 24 Mar 2021
Very nice!
It's a darn shame that
not(NaN)
does not yield NaN, because "NaN cannot be converted to logical". If it did, then
~X.^0
would be a sublime 5-character solution.

Sign in to comment.


the cyclist
the cyclist on 22 Feb 2021
Edited: the cyclist on 22 Feb 2021
First attempt:
X = [-inf, -1, -eps, 0, realmin, 2, 1+i, pi, flintmax, realmax, inf, NaN];
broken = @(x) 1./((isnan(x)-1)/0); % 19 characters
broken(X)
ans = 1×12
0 0 0 0 0 0 0 0 0 0 0 NaN

the cyclist
the cyclist on 22 Feb 2021
Second attempt ...
X = [-inf, -1, -eps, 0, realmin, 2, 1+i, pi, flintmax, realmax, inf, NaN];
broken = @(x) 0./(isnan(x)-1); % 15 characters
broken(X)
ans = 1×12
0 0 0 0 0 0 0 0 0 0 0 NaN
  2 Comments
the cyclist
the cyclist on 23 Feb 2021
I overlooked the obvious improvement to this one:
broken = @(x) 0./~isnan(x); % 12 characters
I like the fact that this one avoids the annoying denominator parentheses

Sign in to comment.


Jan
Jan on 24 Mar 2021
X = [-inf, -1, -eps, 0, realmin, 2, 1+i, pi, flintmax, realmax, inf, NaN];
V = [0, NaN];
F = @(X) V(isnan(X) + 1);
F(X)

John D'Errico
John D'Errico on 22 Feb 2021
Edited: John D'Errico on 22 Feb 2021
I'm always amazed at how many ways there are to accomplish anything in MATLAB. Here was my effort. It took me some time to think of my solution - why I posted the question. I had to find a call that would return a real number for any inputs, including inf and -inf. But I still needed it to return NaN for NaN input. Then i could just multiply by 0. atan fit perfectly. And since 0*NaN is still NaN, this was my solution:
X = [-inf, -1, -eps, 0, realmin, 2, 1+i, pi, flintmax, realmax, inf, NaN];
unbroken = @(X) 0*atan(X);
unbroken(X)
ans = 1×12
0 0 0 0 0 0 0 0 0 0 0 NaN
Only 9 characters. But I am sure there is a better solution yet. And mine might not be terribly computationally efficient.
(Sadly, things like 0*sin(X) fails, because sin(inf) is NaN. But that would have saved a character.)
  2 Comments
Gustavo Lunardon
Gustavo Lunardon on 24 Mar 2021
Edited: Gustavo Lunardon on 24 Mar 2021
X = [-inf, -1, -eps, 0, realmin, 2, 1+i, pi, flintmax, realmax, inf, NaN];
broken1 = @(X) 0./X.^0;
broken1(X)
Also works, 7 characters
X = [-inf, -1, -eps, 0, realmin, 2, 1+i, pi, flintmax, realmax, inf, NaN];
broken1 = @(X) 0*X.^0;
broken1(X)
Works as well with 6 characters
Advantage is that you can put any number different than zero in the other elements by changing the multiplier
Stephen23
Stephen23 on 24 Mar 2021
@Gustavo Lunardon: very neat. Please add this as an answer!

Sign in to comment.

Tags

Community Treasure Hunt

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

Start Hunting!