IMHE this is the kind of edge that you know only because you've been bitten by a bug once.
It's the same with floating point numbers. You may know that the representation is not absolute, that you can end up with NaN. But I found that I only knew it viscerally after I banged my head on bugs related to these.
Of course, that could be provided by Comp Sci ou Comp Eng curriculum, but time is finite...
In the 5-10% of engineers who saw the problem, how many had experienced it once themselves before?
So you are an Android engineer and you deal with ints a lot. Screen coordinates are ints on Android, so if you think the range of an int is "256" how do you think your app works at all?
This question reveals to me one of the most important things I'm usually looking for when hiring: natural curiosity. A software engineer should be curious about things he or she is dealing with. And that starts with the most trivial things like "what is an int really?" and then moves on to other concepts like: under the hood, what is a function?, what is a virtual method? what does `await` really do? And so on.
A good engineer should know how the computers work, and I don't know why this should be even questioned.
Whether or not this is a completely ludicrous answer depends entirely on how you presented the question (i.e. whether or not it was clear that you're talking about java instead of asking a more general question).
For example, in C, the int type can be as low as 16 bits in size, yielding "65 thousand-something" possible values in the worst case. So that could be a reasonable answer as the guaranteed range of values for an int. And even in an android interview, C(++) can conceivably be the assumed context if the previous questions have been NDK-related.
> Let alone it's not even a range!
I feel like it's not a particularly uncommon shorthand to refer to the extent of a range of values that something can take as "the range" of that something.
Wrong both for worst-case C and for "16 bits in size": the actual maximum is "32 thousand-something" (specifically 32767 in 2s-complement and also in most of the stupid representations (like 1s-complement or sign-magnitude), although there might be some that have, eg, 32768). They also have a minimum of -32768 (or -32767 or otherwise for some of the stupid representations).
You could intepret it as "65 thousand-something" values between the minimum and maximum, but that strongly implies that the minimum doesn't need to be specified, which only works for unsigned integers (which C int is very much not).
"256" is a ridiculously bad answer on multiple levels. Believe me, I heard it from more than one Java developer with a CS degree and at least 5 years of experience at the time.
I am not disputing this point, I agree with it.
I am saying there is a difference between knowing int can overflow or knowing that floating point numbers are imprecise, and being attentive when you read `a + b` or `a == b` with float. I believe only experience can teach that (such experience may or should be provided by school).
Because while it is easy to be bitten by this on at 16 or 32 bits, if it happens at 64 bits (1.8446744e+19) it's almost certainly an abstraction error like arithmetic on identifiers rather than values.
Back around 2010, I wrote some code for the first time in a very long time and that code initialized a 10,000 integer array and my first thought was "that's too big to work." Kilobyte thinking in a gigabyte future.
To a first approximation, as an interview question it fights the last war...again embedded systems excepted.
int avg(int a, int b) {
if ((a < 0) != (b < 0)) // not the same sign?
return (a + b) / 2;
else
return a + (b - a) / 2;
}This will round (-2,-1) to -2, i.e. away from zero. For comparison, if we perform the canonical (a+b)/2 instead it will round to -1, i.e. towards zero.
Now, the problem statement does not tell us how to round, so you’re technically correct, but the inconsistency bothers me.
Edit: never mind, this can overflow too
I'm asking as I doubt "know about how computers work" is necessary for most tech roles.
I get it, having technical expertise is important, but still ...
Is it though? It just tells you if they know the trick or not. At that point you might as well have them fill out a form and use the score from that to hire/no hire.
It doesn't tell you if they understand what RAM is or storage (HDD/SSD/etc) or the various buses on a motherboard or pretty much anything about how a computer works. For the example given in the article, it's pretty rare for (a+b)/2 to overflow since the default ints end up being 32 bit (article calls out 64bit tbh) and your parameters are [-1000,1000].
---
> 90-95% of engineers don't even see a problem with (a + b) / 2 until you tell them about the overflow, let alone find a solution for it.
In my experience a similar percentage can't write a working DFS which I think is much more work related than midpoint.
def is_triangle(a, b, c) -> bool:
...etc...
One of the things an ideal candidate would realize is that the triangle inequality needs to apply (a + b >= c (for all permutations of a,b,c)), and that if a developer naively implemented the above via: if a + b < c:
return false
it'd run into this exact problem.I'd thought this question had gotten stale / overdone, but perhaps it's still a great interview question.
First of all, doesn't it also need to have the condition (c > a - b)? Maybe you just left this part out?
Secondly, you're worried that (a + b) could overflow. The triangles in your applications are just THE MOST EXTREME! That's how cool your application is! You have the MOST EXTREME TRIANGLES!
But wait! When are you dealing with integer lengths of triangles? You never specified they were integers. In 99.99% of all real-world applications dealing with triangles, their coordinates are floating-point. I think it's fair to say overflow isn't nearly your biggest arithmetic problem with floating points -- rather, once you get to extreme (and extremely different) exponent values, you have all sorts of accuracy and loss issues, long before overflow becomes an issue. Do you expect the candidate to also handle the case where one side is 1e-155 and another is 1e274? Because otherwise their code is "wrong"!
So left unspecified, your "gotcha" is completely ridiculous and just a mind-reading trap to arbitrarily filter out people who don't think exactly (and erroneously) like you do!
Or maybe you did mean that the sides of the triangle are constrained to integer lengths? That would be extremely unusual, so you absolutely need to specify that. But if you're constraining the side lengths to integers, are you also constraining the vertex coordinates to integers? It would be extremely strange to require integer lengths but allow floating-point coordinates; but it would also be extremely strange to only allow integer coordinates, as most triangles with integer coordinates have a least one non-integer side length! And it doesn't sound like a trivial problem at all to find whether a given set of integer lengths could form a triangle on an integer lattice, but that seems to be what you maybe think you're asking? Do you even know what you're asking?
If integers are involved at all, it's far more likely that the coordinates are integers, but the side lengths are floating point.
What a tremendously awful interview question. I really hope your description is just extremely limited and flawed and mis-remembered, because if that's the actual question, you are perpetuating the "arbitrary leetcode interviews" problem that competent software engineers always complain about when they look for jobs.
Seems like you can cover the corner case easily enough:
x/2 + y/2 + (x & y & 0x01)
A few more operations than the article though so not the most efficient solution.This returns -1 for x = INT_MIN and y = INT_MAX were the answer should be 0 (for an example). so not a correct solution
The C standard says: When integers are divided, the result of the / operator is the algebraic quotient with any fractional part discarded (This is often called ‘‘truncation toward zero’’).
So it should be 0 (as per C standard, not sure what C++ standard says)
2^64 is 1.8446744e+19
To a first approximation, if your application is overflowing 64 bit integers, the problem is in the abstractions not sloppiness in low level details...something like doing arithmetic on IPv6 addresses.What I mean is that it's one think if you specify a 32-bit or 16-bit architecture because the job entails low level programming with that constraint.
But entirely another think if it is used as a general test of software engineering skill because on the one hand, now I know the trick of the trick question and you wouldn't hire me based on my software engineering chops.
And on the other hand, in the vast majority of cases, the solution that might overflow is not just the simplest thing that might work, but it will also work well enough for the business purpose and be easier to support and maintain in the code base.
Finally, handling problems like this culturally during development and after-action debriefing is better healthier than how-many-golfballs at the interview stage...like I said, I know the answer.
When you write a bit of code, you naturally have in mind the realistic range of values you'll be working with. Even if it's just within 4 orders of magnitude. You know whether you're dealing with thousands or quadrillions. In the extremely rare case it's the latter, then you start worrying about this. You just can't worry about being 10 orders of magnitude off in your assumptions all the time -- that's what Exceptions are for. Holy crap, this software is dealing with values ten orders of magnitude different than you programmed for!? Absolutely throw an exception in that case, because it requires a complete rewrite of that area of the code.
Yes, if you're writing midpoint in a language-standard math library, it should work for all possible inputs. But the point of looking at toy problems in software engineering blogs is to inform us how to write our much more complicated, much more domain-specific code, and these lessons just don't cleanly transfer into that world.
Just to pretend to be an engineer a bit longer, 64 bits still provides a few orders of magnitude of overflow headroom for integer subtraction, addition, and division.
Multiplication is another story and might come up if you’re rolling your own cryptography. But then you have two problems since 64bits isn’t big enough.
Or rather three since you are rolling your own cryptography.
Why is that wrong?
In general, even with floating point numbers you can get different results by rounding implicitly or intentionally the intermediate values versus the end result.
But in computing, assuming we are only dealing with integers, 1/2 == 0. So 1/2 + 1/2 returns 0.
"This will be too low if the original addition contained a carry from bit 0 to bit 1, which happens if bit 0 is set in both of the terms, so we detect that case and make the necessary adjustment."
return (a / 2) + (b / 2) + (a & b & 1);
In Rust you can actually do this using the checked add/sub/etc methods. It's one of the things I really appreciate about the language. By default, it panics on overflow in debug builds. You have to use the wrapping variants to declare that's intentional.
How about:
if a > b {
mid = (a-b)/2
} else {
mid = (b-a)/2
}
There must be a way to do it without the branch?
edit: Yes, in the article, I'm an idiot. if a > b {
mid = (a-b)/2
That only avoids overflow on unsigned types (where it would be called wraparound) ...0: Assuming your language rounds integer division and modulo correctly, ie that `i%array_len` is reliably a valid array index. C has problems here when i (respectively: a or b) is signed, but that doesn't matter in the sensible case, where you always index everything with size_t.
The majority of programmers have no idea what an int is in their favorite language and what its range is (roughly).
Then the majority come up with (a / 2) + (b / 2) until they run the unit tests and realize it's wrong.
And so on and so forth, with this question you can uncover layers upon layers of trivial (non-)knowledge.