124 views (last 30 days)

Show older comments

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)

Though it is close. It suffers because 0*inf generates a NaN. An as pretty one is:

broken2 = @(X) X - X;

broken2(X)

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)

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.)

Stephen Cobeldick
on 22 Feb 2021

X = [-inf, -1, -eps, 0, realmin, 2, 1+i, pi, flintmax, realmax, inf, NaN];

Y = 0./(X==X)

Gustavo Lunardon
on 24 Mar 2021 at 18:47

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

the cyclist
on 24 Mar 2021 at 18:58

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.

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)

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)

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

John D'Errico
on 22 Feb 2021

Edited: John D'Errico
on 22 Feb 2021

Stephen Cobeldick
on 24 Mar 2021 at 17:03

@Gustavo Lunardon: very neat. Please add this as an answer!

Jan
on 24 Mar 2021 at 16:50

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)

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

Start Hunting!