Tricky interview question

I was asked at interview :
"What is the purpose of such function ?"

template<typename T>
bool foo(const T& t)
{
return t == t;
}

And I don't have any idea about this.
I am not sure, but two things come to mind:

For floating point values, you can test validity (not NaN, etc) by comparing with itself.

The function can also be used to halt compilation if T does not have an == operator defined for some instance of foo( my_type_object ).

Or, if you want to get freaky, using a SFINAE trick to get introspective about T.


Maybe someone else (JLBorges?) will have a better idea than I.
It checks if T has an equals overload.

I can't think of any other purpose.
The interviewer was probably looking for one of several things:

  • You already know the answer(s). Good job!

  • You do not know the answer, but can suggest good ways to learn it, including
     • asking your team mentor
     • asking google, stackexchange, etc
     • asking the documentation
     • examining the code base to find usage examples
    This works toward understanding how a potential employee will handle stuff
    he or she has never seen before and what kind of thought process and work
    ethic can be expected when dealing with new concepts and code. Good job!

  • You have no idea and ask, “What is it for?”
    Alas, you probably don’t get the job.

• You have no idea and ask, “What is it for?”
Alas, you probably don’t get the job.

You are quite right, I didn't get this job, but I honestly said that I don't know the answer. Unfortunatelly, I don't have mentor at team, tried, but unfortunate, create proper google request, tired to scroll down any helpful doc and finally haven't found any useful example about it, that's why I've come here.


For floating point values, you can test validity (not NaN, etc) by comparing with itself.


Great idea. I think this what he meant. We were talking about overflows and flat type sizes etc.

Thanks a lot !
Last edited on
• asking your team mentor
• asking google, stackexchange, etc
• asking the documentation
• examining the code base to find usage examples
Fifth option: do a blame and ask the author what they were thinking naming such an odd function 'foo'.
I'm a 50 something, developer, programmer (engineer), and I've hired a lot of programmers in my years.

I really despise silly interview questions. It has no practical value, IMO.

For this code, given any type that behaves like an object OUGHT to be behave, this always returns true and serves no useful purpose.

As one put it, it could generate an error if a type is used that does not provide the == operator.

I have no reason to think that is useful, really, because code that used the == operator on such a type in an actually useful context would also generate an error. Perhaps someone generates a library and this is a compile time test that a candidate object qualifies for a template library, but this is too "cute" and not important enough to bother doing that.

Now, maybe I'm missing some esoteric bit of technicality, but frankly there is no purpose. It should either always be true, or any object that could return false (which means the == operator changes the test condition as it executes) should not have an == operator (and I can't quickly contrive a scenario where such a result would ever exist, and that's after decades of writing in C and C++).

Even if there is some contrived purpose, it's nonsensical. Consider comparing usage example against simple code:

1
2
3
4
5
6
7

SomeClass t;

if ( foo( t ) ) { .... }

if ( t == t ) {....}


The second version is less work, assuming I wrote foo.

If I did not write foo, the second version shows the absurdity of this comparison without having to know foo itself is absurd.

I'll tell you this from my own experience in the field. If I were looking for a job and this is the question asked during an interview, it would tell me I probably don't want to work for the person fashioning such a question.

Since I'm convinced the code has no purpose, I would expect the answer would be that it has no purpose. If I'm trying to see if I'm talking to an applicant who really doesn't have knowledge of C++, I'll see that fact from more meaningful questions. If I were trying to provoke a professional into an argument to see how they argue, how they are triggered, then this is a psychological ploy that is best performed by a psychologist and not me. Same for exposing someone who simply doesn't want to tell me something doesn't make sense.

If you didn't get the job, consider that a reasonable result given the context. You may need a job, but you don't want one working for someone willing to BS an interview question like this.



Interviewers are not interested in writing correct, useful code. They are interested in picking your brain.

The fact that it was named “foo” is a pretty heavy-handed indication that the interviewer knew full well that this was a contrived question; part of the answer is the interviewee recognizing it as such.

Whether we dislike these kinds of questions or not, the absolutely wrong answer would be to pick on its name (demonstrating a lack of CS cultural knowledge and convention) or to call it nonsensical and trying to justify writing it better (demonstrating a lack of contextual awareness).

In both cases, it also demonstrates someone who is inflexible in their thinking — not team candidate material, regardless of experience.

@Niccolo
It has already been established that it has at least one valid purpose. Claiming OUGHTs means nothing and is a dangerous attitude to have when programming. Any specific programming language / OS does what it does regardless of what individuals think it ought to do. I ought to have a swimming pool in my back yard, but the city thinks I ought to should have a fence, too.

A high quality programmer/SE/whatever candidate should know how things are specified to behave, how things actually behave in at least their given platform(s) working environment(s), and how things are likely to survive time. That, or they should demonstrate ability to learn those things without being coddled or coerced.

    x == x is well-defined for IEEE 754 floating point values (Quiet NaN)
    https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN

The Standard Library even lets you know whether you have a signaling NaN or a quiet NaN with std::numeric_limits<>. https://en.cppreference.com/w/cpp/types/numeric_limits


Not to mention, little functions like foo() are wholly correct. Most modern systems subscribe to IEEE 754, but many in the wild still do not. By making a global function to test for NaN, you have instantly reduced maintenance costs significantly. In three years when your rockstar app is pulling in billions, and the CEO says to you, “We need to look into porting this to that new iHeadband using that oddball embedded processor, oh, and to a PDP-11 too,” you don’t need to start putting your résumé out because you’re royally screwed.


Further, you are placing judgements on the interviewer for which we have no evidence. Sure, he could have just asked tabaluga how he would check for NaN, but he can definitely learn more about a candidate with this question.

If I were the interviewer I would certainly not be hiring you, because your answer is combative and, frankly, full of BS about how things work and how things should be. You may have some experience, but you have failed two-thirds of the question. It is BS because your opinion on the validity of the question is uninformed — you know nothing about the position or the technical matters involved. You’re just being arrogant by guessing the worst possible way.


I am not an expert interviewee by any means, but I wouldn’t put any wrong to joking about the kind of effort it takes to contrive code for interviews and see how the interviewer responds, as you are interviewing the company as much as the company is interviewing you.
Last edited on
@tabaluga
Where you really failed (sorry to sound mean, I am not trying to be) is when you misunderstood the purpose of the interview and the resources available to you.

In the interview, you can simply say
You know, I have never seen that construct before. If I were to find it in the code base I would want to know what it is.

I presume that I would have a mentor assigned, for the first month or two at least, who could point me in the right direction? Assuming I couldn’t figure it out by googling around terms like ‘IEEE floating point self comparisons’ and looking through the code base and documentation to see how it is used.

Since its name is cleverly obscured, what does it do, by the way?

Hope this helps in your next interview.
Last edited on
I couldn't figure out the answer until reminded about NaN. What I did notice is the == and the ICS to bool.

Describing the purpose of
1
2
3
4
5
template<typename T>
bool foo(const T& t)
{
  return t == t;
}

Requires identifying self-comparison as an idiom entirely out-of-context.

As-is, the onus is on the interviewer to provide that context (e.g., of a numerical algorithm) that might be otherwise be inferred from the code. This isn't ideal; hopefully you get a helpful interviewer.

Indeed, if the word float appears in the question, the answer should be obvious. Describing the purpose of
bool bar(float t) { return t == t; }
Requires nothing more than an understanding of float.
Last edited on
@mbozzi
OP had context in the interview: he was talking about numeric representations.

That, and the t == t idiom really only has a few uses, the primary of which is testing for NaN; so it really is a either you know it or you don’t kind of test (and a follow-up: what will he do if he does not know, as <most programmers?> don’t — will he make the connection that this has something to do with numbers?).
Not all interviewers are any good in that role.

Don't beat yourself up if they ask you crap, or think you've answered incorrectly, when in fact it's the interviewer that's wrong.
...and yet we still have absolutely no evidence that this particular interviewer was deficient.
@Duthomhas

Clearly I hit a nerve with you, and I'm not sure why. Your post drips with passive aggression and hostility, directed at me.

I briefly pointed out that I'm experienced, but what I didn't make clear is that I formed my own firm over 25 years ago. Here, I'm the boss. Either I do the hiring, or one of my more trusted employees does that. Since my firm occasionally consults with clients expanding their development staff, we conduct interviews for them.

Perhaps you missed it, but my post was not about the applicant's reply, but about the interviewer relative to this question. As the owner of my own firm, as a developer of decades of experience on topics that range from engineering and manufacturing, robotics, 3D rendering engines, physics engines, audio workstations, and a lot more, I claim to have earned the right to make the judgment that this question is silly. If one of my employees relied primarily on questions like this for interviews, that person would no longer be conducting interviews for me.

If that individual, defending their motivation, presented what you posted, I’d be further convinced they shouldn’t conduct interviews until some remedial study is completed. That’s because ‘t==t’ is not “a thing”, it doesn’t work because it isn’t reliable.

I suspect this came into view from social media; an example of the spread of misinformation. Someone reading IEEE’s paper(s) noted that the way to test for NaN is given by IEEE because of how NaN compares. What IEEE was really specifying was the behavior of the processor complying with IEEE’s format for floats and doubles. It was NOT a description of how C or C++ should test for NaN, but it seemed obvious to someone that ‘t==t’ would be the C construct for doing that. Unfortunately, they didn’t think through the problem.

C and C++ make no guarantee of the format of floating point values. The spec is silent on the point. The concept of NaN comes from the implementation, not the language spec. The behavior of the platform is outside the scope of the language. Under certain conditions ‘t==t’ may well provide a signal that a NaN is found, but it is not guaranteed. A huge array of compiler configurations and platform choices will undermine the reliability of this idiosyncratic idiom. The compiler is free to optimize this test into nothing. It may, under at least as many configurations is it doesn’t, optimize this test away. The code emitted may not even examine ‘t’ at all, and it may do that silently. This can be demonstrated as fact on a number of compilers, including LLVM, GCC and MSVC. Try it. Turn on “fast” math and check the assembler output of an optimized build. The test is ignored, not even emitting code for the “false” branch after it.

There will be a great many highly trained, highly competent engineers that will not recognize ‘t==t’ as a valid idiom, or even think it does anything. As does the compiler itself, they may note the algebra of this statement always returs true, and consider it a trivial bit of nonsense. It only works under the narrow conditions of strict IEEE behavior regarding floating point math, and even then only under certain conditions of optimization choices. No professional would rely on this code as a test of NaN, and many wouldn’t even consider the idiom as one. They’d be correct.

C99 introduced isnan, which is a far more appropriate way to detect NaN. The source of GCC, LLVM and MSVC do not use this idiom. They test the bit pattern of the float for the NaN signature, and that offers the hope that the compiler will choose to implement a method appropriate for the compiler’s configuration at compile time. There were some few years of flaky performance of isnan in GCC, especially as it was appropriated by C++ programmers for years until isnan became part of the C++ standard in C++11. When these libraries check for the bit representation of NaN, they do so in a way that does NOT end up optimized away.

Notice, too, that the C and C++ standard library does not implement isnan as a template function. That is because it hardly makes any sense to do so. There would be nothing but specializations for the two use cases, so it is simpler and more logical to implement isnan as a simple non-member, non-template function overloaded for floats and doubles. One might think a template function could be applicable to classes like vectors or quaternions, but that returns us to the point that the representation is a rather specific, platform or implementation dependent approach with no awareness of vectors, quaternions or the myriad other potential classes comprised of floats or doubles. It is unlikely that the ‘==’ operator would call isnan for vector or quaternion classes, and the test for ‘q==q’ for a quaternion is no more likely to detect NaN than for a float or double.

Beyond this, what does testing for NaN do? NaN is the result of an operation that’s already gone wrong. In the math libraries, the input may be tested for infinity and nan (many of GCC’s library functions do this), and propagate them to the output. This paradigm makes more sense than a test after the fact, because by the time application level code could note a NaN has resulted, the problem already happened, and correction may not be possible. NaN could result from division by zero, but that usually generates a processor exception (always does on many platforms), and that can only be caught, reliably, on Windows with SEH enabled. By then, it doesn’t matter if NaN was the result, because the exception already explains why. Passing a negative value to sqrt, or infinity to trig functions (and a handful of other, similar operations) can result in NaN without generating an exception, but there, too, the problem already happened. The correct way to deal with these is to prevent them. The developer should not divide by zero, should not pass negative values to sqrt, etc. These can either be caught before they cause the problem at runtime, or, where practical, caught during debug and check build tests through an assert mechanism.

So when you react to my post as combative, you mistook me for an applicant. I'm not. I'm the interviewer's boss. However, if a question is silly, I would want to hear that from the applicant. If the interview relied on several idiomatic "gotcha" questions, I'd be most interested if they became irritated and said so.

Now, I understand the notion of wanting team players, but if everyone on the team is a nice team player you may end up without a leader. Team players are fine, but when someone has a point they really believe in, and they're not being understood or heard, or worse ignored, I expect a little tension. I do expect that to get resolved, but anything that looks like BS should be called BS.

On that point, when you call my post BS, you were incorrect. That idiom should be scrubbed from your memory, and replaced either with isnan from the standard library, or the type of test found in the source for it. Your own soapbox soliloquy was the BS. You failed to differentiate an IEEE concept from a language concept. That unreliable, idiosyncratic idiom is not about the language, it is about the CPU’s behavior testing floating point values under strict compliance with IEEE implementation, which the language may well even ignore under a wide range of conditions.

Since it is clear that the idiom 't==t' is not a valid way to test for NaN in C++, the OP's example question would tell us one thing if the applicant thought it was a valid way to test for NaN. It would tell us they're misinformed.
I will consider myself very humbly schooled.
The C++ standard contains ISO/IEC/IEEE 60559:2011 (that's the same floating point standard) as a normative reference. std::numeric_limits<T> will tell you if the implementation claims a particular floating point type is compliant to that standard. If a type does comply, our assumption about self-comparison of NaN is entirely valid.

As long as the implementation claims compliance, all you need to do is avoid telling the compiler to make assumptions that break your code.

There were some few years of flaky performance of isnan in GCC, especially as it was appropriated by C++ programmers for years until isnan became part of the C++ standard in C++11. When these libraries check for the bit representation of NaN, they do so in a way that does NOT end up optimized away.

Not quite. The test is not removed because it violates the law of identity. It's removed because overly-aggressive optimization flags allow the implementation to assume that all floating point math is finite.

Examine this program:
1
2
3
4
5
6
7
8
9
10
11
12
#include <limits>
#include <cmath>
#include <cstdio>

int main()
{  
    static_assert(std::numeric_limits<double>::is_iec559, 
                  "double is IEEE754-compliant");
                  
    std::printf("std::isnan(NaN) == %d\n",
                std::isnan(std::numeric_limits<double>::quiet_NaN())); 
}

With the right compiler flags (e.g., -ffast-math or just -ffinite-math-only), the program might report 0 (optimizing out the test along the way):
http://coliru.stacked-crooked.com/a/bc0b7caecc80cae0

Please notice that this makes a liar of the implementation, independent of what the NaN test looks like. If a program depends on strict compliance, "fast math" is incompatible, and it best be left disabled.
Last edited on
@mbozzi,


If a type does comply, our assumption about self-comparison of NaN is entirely valid.

As long as the implementation claims compliance, all you need to do is avoid telling the compiler to make assumptions that break your code.


I can't agree. The type isn't what makes NaN comparisons unequal. The type, or the format of the floating point, doesn't change over various floating point optimizations relative to the discussion.

What changes is the compliance with behavior. One of the issues 'relaxed' in fast-math is the propagation of NaN. That does not imply NaN doesn't exist, or wont be returned from sqrt(-1), but it does mean strict compliance with all forms of propagation of NaN aren't going to be considered, and 't==t' depends on one of the rules of behavior to be strictly enforcement. That makes it brittle. Determining a NaN exists does not have to depend on how the compiler is configured, but 't=='t does.

What you've found is a bug in an implementation. When I checked on VS2015/17/19, LLVM (recent, maybe not the most current if they've updated in the last two months) and GCC (similarly, could be as much as two months old), the example you post above executes correctly under all the permutations I tried of optimizations which cause the idiom 't==t' to fail. Factually, I've not seen std::isnan fail under any circumstance on these 3 compilers, though I can't say I've tried every possible configuration. That makes me curious about the implementation on the compiler you used, but alas, I'm not digging that deep (didn't recognize what compiler that is). It would be hilarious if they used 't!=t', which is nearly as common an alternative to 't==t' found in discussions on the topic.

Think about that a moment.

It confirms what I've been trying to communicate here.

std::isnan is far more likely to work than 't==t'. If std::isnan doesn't work, then, as you pointed out, this makes the implementation a liar (another way of saying it has a bug).

In GCC, isnan is implemented with:

1
2
3
4
5
6
7
8
9
int __isnan (double x)
 {
   int32_t hx, lx;
   EXTRACT_WORDS (hx, lx, x);
   hx &= 0x7fffffff;
   hx |= (uint32_t) (lx | (-lx)) >> 31;
   hx = 0x7ff00000 - hx;
   return (int) (((uint32_t) hx) >> 31);
 }


That's not likely to be optimized away under most any circumstance.

In MSVC, it's done with:
1
2
3
4
bool isnan(_In_ _Ty _X) throw()
    {
        return fpclassify(_X) == FP_NAN;
    }


MSVC uses the same fpclassify method for determining normal or infinite.

The source for fpclassify goes like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int 
__fpclassifyf(float f)
{
	union IEEEf2bits u;
	u.f = f;
	if (u.bits.exp == 0) {
		if (u.bits.man == 0)
			return (FP_ZERO);
		return (FP_SUBNORMAL);
	}
	if (u.bits.exp == 255) {
		if (u.bits.man == 0)
			return (FP_INFINITE);
		return (FP_NAN);
	}
	return (FP_NORMAL);
}


Which is also not likely to evaporate over fast-math or other optimizations, and is a slightly cleaner 'bit level' approach than GCC's, which makes it more readable.

't==t' is an obvious optimization candidate, and the setting of precise or strict math settings is all that stop the compiler (when optimizing) from treating this idiom as an obvious constant expression that's always true (as you mentioned, from logic, algebra and most programming languages generally, it is the very epitome of the law of identity). It should be clear it's brittle as a C++ expression used to test for NaN. I can't regard fast-math as an esoteric or uncommon optimization choice.

Yes, it CAN work, but that doesn't mean it should be considered valid. One of the stipulations in a wide range of coding standards gives us a rule to prefer the standard library over 'hand built' solutions.

I assert it depends on what one really means by that word 'valid'. Does 't==t' work? Under certain circumstances it will because the compiler emits code which causes the comparison of a NaN in the CPU, and the processor will therefore report the pattern of a NaN's comparison. Note, however, that the standards speak of a NaN comparing to a NaN. They are not actually giving a C programmer the hint of comparing a variable to itself. They're speaking more generally how NaN's compare. That does not impose a requirement that the compiler guarantee that 't==t' isn't trivially optimized into 'true'. The compiler's setting to strictly comply with IEEE behavior keeps the optimizer from employing the rather obvious peephole optimization due to the logical and algebraic implication of 't==t'. It's worth noting that where such optimization choices cause 't==t' to fail to detect NaN (because it doesn't even test 't'), 'p==q', where either or both are NaN, returns correct results where I've tried it on MSVC, GCC and LLVM. This suggests these compilers aren't scrapping all compliance with IEEE, just that they're recognizing 't==t' for trivial optimization without concern for THAT idiom's meaning to programmers using it.

Consider what is being stated in the code. Is the intent of 't==t' clear? Is it obvious? No. It isn't. Is the source for std::isnan in either example above clear? MSVC's looks fairly good, while GCC's is a tad messy, though clearly GCC is examining the bits of a float for 'something' it appears to recognize. The name of the function is otherwise somewhat clarifying. None of that applies to the text 't==t' or 't!=t'.

What is happening in these code examples?

For 't!=t' or 't==t', where t is a floating point type, the processor is called upon to perform a floating point comparison. Such a comparison is performed as a subtraction, where the result is discarded but the flags are used to figure out the result of the comparison. Where a 't' is a NaN, this causes a floating point exception, which is handled in microcode. That's slow on many processors. It would, when it works, use the processor's implementation of IEEE floating point behavior as a means of compliance.

Bit fiddling, on the other hand, can happen in a standard register, won't fire a floating point exception with it's associated performance implication, and focuses determination on the actual CONTENT of the floating point value. This approach better fits the nature of what is being determined. It also does not imply an ambiguous motif that the optimizer might not really 'understand'.

While you have found an implementation where std::isnan failed, I can't repeat that finding on other compilers. That's highly suggestive (if not actual proof) that std::isnan is more likely to be correct. Where it isn't, that should be considered a bug for the compiler vendor to correct. We should expect the engineers writing a library for a compiler put considerable effort into the implementation, which is why many coding standards direct programmers to prefer the standard library where applicable.

There is, however, one thing that occurs to me that would cause the test 't==t' to execute on the processor no matter what optimizations where chosen. One could write that as inline assembler.


Last edited on
To avoid confusion, this behavior occurs under the GNU implementation in particular. When I mention fast math, I mean GCC's -ffast-math or some of the options it implies.

t==t is an obvious optimization candidate, and the setting of precise or strict math settings is all that stop the compiler (when optimizing) from treating this idiom as an obvious constant expression that's always true (as you mentioned, from logic, algebra and most programming languages generally, it is the very epitome of the law of identity). It should be clear it's brittle as a C++ expression used to test for NaN. I can't regard fast-math as an esoteric or uncommon optimization choice.

I agree that fast math is a quite common option. However, it still imposes additional constraints on our program, which we have violated.

It's not very meaningful to discuss the behavior of an implementation whose contract we've broken. NaN is not a finite value, and we've explicitly instructed g++ to assume that such values do not appear. The incorrect result from std::isnan() is not a defect.

To put it another way, I've demonstrated undefined behavior.

See, e.g., the discussion here:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84949

That snippet was meant to illustrate that some aggressive optimizations are incompatible with IEEE754-compliant floating-point in general. The GCC documentation goes as far to say that -ffast-math may yield wrong results if your program depends on strict interpretation.
https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

That makes me curious about the implementation on the compiler you used

In particular, the compilers used are LLVM's clang++ 5.0.0 and GNU's g++ 8.2.0 using the same GNU libstdc++. The revision date of the library sources is July 26, 2018, corresponding to the GCC 8.2.0 release. That date comes from the value of the library macro __GLIBCXX__.

In GCC, isnan is implemented with [some source code]

GCC's isnan() is intrinsic to the compiler. The library implementation would serve as a fallback if the built-in is unavailable for some reason.
https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc/Other-Builtins.html#Other-Builtins

The real distinction between the floating-point classification stuff and the relational operators are that the latter may have side effects in the floating-point environment (i.e., only if you said #pragma STDC FENV_ACCESS on). Otherwise, the expressions are equivalent.

Annex F in the C standard draft says:
F_8_3 wrote:
The statement x == x is false if x is a NaN

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
Last edited on
Topic archived. No new replies allowed.