Java quiz



  • What will be the value of x afer the following code is executed?

    int x = 10;
    do
    {
       x *= 20;
    }
    while (x > 5);
    

    Options offered:

    • 10
    • Infinite loop
    • 200
    • The loop will not be executed, the initial value of x > 5

    Now, it's not so much that the right answer isn't offered, it's the number of (alleged) programmers that insist that their wrong answer is, in fact, right..

    (http://archive.is/iQ027 at the time I caught it.)


  • Impossible Mission Players - A

    I knew the loop would stop as soon as it overflowed, but I wasn't going to calculate what negative number it would have overflowed to.

    Re-upload the screenshot in case archive fails

    0_1507705750388_45634e3b-d55d-414e-a087-468f485b9de9-image.png

    Spoiler

    0_1507705542268_03c2ff8a-2e07-4e2a-9a41-2eecfb536f34-image.png

    .

    Edit: Though to be fair, the last answer is literally inane...

    The loop will not be executed, the initial value of x > 5.



  • Side question from a non expert.

    Is there ever a good reason to allow int overflow?

    Should the compiler give a warning or should the runtime give an indication?



  • @pjh If this was C/C++, the right answer would be it will do whatever the hell the compiler pleases. However this is Java and I believe they removed most of the pentanasagrams except probably few dread-related ones, so it should be actually well defined there.


  • Impossible Mission Players - A

    @tsaukpaetra said in Java quiz:

    answer

    I suppose what they're really testing for in this question is theoretical failure in understanding semantics and not the actual pedantics of the implementation of int.

    A tests your knowledge on the fact that the do portion will always execute when placed first, B ignores the fact that ints have an actual maximal limit, and C tests your knowledge that the do will execute first but also on the mistaken idea that the while cannot apply to the do if it comes afterward. D tests for the mistaken idea that the while portion always applies first and controls the do portion, regardless of syntactical placement.



  • @ben_warre said in Java quiz:

    Is there ever a good reason to allow int overflow?

    Sometimes yes. Besides for checking whether it happened, there are cases related to bitwise operations. You'd normally do those on unsigned, but Java does not have unsigned types (C/C++ defines overflow for unsigned, but not for signed).

    @ben_warre said in Java quiz:

    Should the compiler give a warning

    Unless the overflow happens during constant folding, the compiler can't tell, so it can't warn either.

    @ben_warre said in Java quiz:

    should the runtime give an indication?

    It would be nice if you could specify whether you want to permit it or not, on per-variable basis, and asserts were inserted where you didn't permit it. Rust has special wrapper types for that and you can define similar in C++ and even in C#, but Java with its lack of user-defined value types is not really up to the task.



  • @bulb said in Java quiz:

    Unless the overflow happens during constant folding, the compiler can't tell, so it can't warn either.

    That's definitely extremely likely, but could a sufficiently-aggressive Java compiler not discover it through loop-unrolling, at least for a trivial loop like this?



  • @tsaukpaetra said in Java quiz:

    understanding semantics and not the actual pedantics of the implementation of int.

    Well, the behaviour is actually defined. The people who understand it will be confused by the provided answers.

    … actually, it would make a lot of sense to add the actually correct answer and give full point for it and half for the “infinite loop” one. Because those who see it is -84901888 need to understand both programming and details of Java, but those who think it's infinite loop do at least understand programming.



  • @gwowen said in Java quiz:

    @bulb said in Java quiz:

    Unless the overflow happens during constant folding, the compiler can't tell, so it can't warn either.

    That's definitely extremely likely, but could a sufficiently-aggressive Java compiler not discover it through loop-unrolling, at least for a trivial loop like this?

    Yes, it could. And a C++ one even actually might. But given it's well defined in Java, Java one probably won't anyway.



  • @ben_warre said in Java quiz:

    Is there ever a good reason to allow int overflow?

    Hashcode calculations spring to mind. Also tight loops where checking for overflow would incur overhead - while most of the time you don't need the overflow behavior to be allowed, you just as often don't need to check for it either...


  • Discourse touched me in a no-no place

    @ben_warre said in Java quiz:

    Should the compiler give a warning or should the runtime give an indication?

    It's well-defined in this case. Java has different semantics to C and C++ in this area. In particular, `int` is defined to be a 32-bit signed 2s-complement value, making multiplication over integers an operation that never fails and always produces a defined value.

    C# might also be similarly well defined. I've not checked (unlike for evaluation order where I have looked in the past, and which isn't an issue in this example).


  • Discourse touched me in a no-no place

    @ben_warre said in Java quiz:

    Is there ever a good reason to allow int overflow?

    In general, there are three options for dealing with overflow.

    1. Just throw the bits away.Java does this
    2. Clamp the value to max-int (or min-int for overflow in the other direction). This is extremely useful in some applications, but isn't how many people's languages actually roll.
    3. Throw some sort of exception. Think carefully if you are asking for this, as it means that nearly every single mathematical operation gains semantics involving failure, and that really screws the compiler on the optimisation front.

  • Notification Spam Recipient

    @dkf said in Java quiz:

    @ben_warre said in Java quiz:

    Is there ever a good reason to allow int overflow?

    In general, there are three options for dealing with overflow.

    1. Just throw the bits away.Java does this
    2. Clamp the value to max-int (or min-int for overflow in the other direction). This is extremely useful in some applications, but isn't how many people's languages actually roll.
    3. Throw some sort of exception. Think carefully if you are asking for this, as it means that nearly every single mathematical operation gains semantics involving failure, and that really screws the compiler on the optimisation front.

    Rust does this right. In debug mode, it panics at integer overflow; in release mode (optimized), it overflows properly with two's complement. And you can use stdlib functions to do math in all 3 methods regardless of mode.



  • @bulb said in Java quiz:

    … actually, it would make a lot of sense to add the actually correct answer and give full point for it

    I'd settle for "None of the above. (Explain)".



  • @bulb I tested the C-version with gcc 7.
    At -O3 optimization, the compiler replaces the do {...} while() with an empty infinite loop.

    I don't know whether that counts as "detecting overflow", or simply being allowed to assume that x > 5 implies that 20 * x > 5 (Which is true because the compiler can assume that overflow doesn't happen [overflow is UB]).

    At -O0 it simply does the arithmetic, and produces the same large negative number as Java.



  • @tsaukpaetra said in Java quiz:

    Edit: Though to be fair, the last answer is literally inane...

    "The loop will not be executed, the initial value of x > 5."

    I disagree. It's a great distractor to see if you're really paying attention. You expect to find a sane condition (relative to what's going on in the rest of the code) and this matches that expectation if you don't notice the direction of the inequality.



  • @pie_flavor said in Java quiz:

    Rust does this right. In debug mode, it panics at integer overflow; in release mode (optimized), it overflows properly with two's complement. And you can use stdlib functions to do math in all 3 methods regardless of mode.

    Because nothing says :doing_it_right: like defined different behavior in debug and release builds!


  • Notification Spam Recipient

    @boomzilla said in Java quiz:

    @pie_flavor said in Java quiz:

    Rust does this right. In debug mode, it panics at integer overflow; in release mode (optimized), it overflows properly with two's complement. And you can use stdlib functions to do math in all 3 methods regardless of mode.

    Because nothing says :doing_it_right: like defined different behavior in debug and release builds!

    You're :thinking_it_wrong:. Overflow in regular arithmetic should be treated as a bug. In debug, to aid you in finding this bug, it will panic when it occurs. In release, to assist your application in running quickly, it does not check math. You should never overflow using mathematical operators; optimization just turns the check off. Conversely, if you intend or at least anticipate overflows, you use the stdlib functions to explicitly define the behavior you're looking for, no matter what that behavior is. This is actually the best possible solution besides Python's of "just don't".


  • Discourse touched me in a no-no place

    @pie_flavor said in Java quiz:

    Overflow in regular arithmetic should be treated as a bug.

    Not if the code is written to work with it. 🏆



  • @zecc said in Java quiz:

    @bulb said in Java quiz:

    … actually, it would make a lot of sense to add the actually correct answer and give full point for it

    I'd settle for "None of the above. (Explain)".

    And force the grader to read the answer instead of letting the computer automatically evaluate it? You monster


  • Discourse touched me in a no-no place

    @homobalkanus said in Java quiz:

    And force the grader to read the answer instead of letting the computer automatically evaluate it? You monster

    That's what the Deep Learning AI is for.



  • @gwowen said in Java quiz:

    I don't know whether that counts as "detecting overflow", or simply being allowed to assume that x > 5 implies that 20 * x > 5

    The later. The former would require giving a warning (“x > 5” is always true in ….).



  • @dkf said in Java quiz:

    Not if the code is written to work with it.

    If the code is written to work with it, it must declare it by using std::num::Wrapping.


  • kills Dumbledore

    @bulb said in Java quiz:

    std::num::Wrapping.

    Read that as std::numberwanging



  • @dkf said in Java quiz:

    @homobalkanus said in Java quiz:

    And force the grader to read the answer instead of letting the computer automatically evaluate it? You monster

    That's what the Deep Learning AI is for.

    So instead of replacing programmers we replace hiring managers?



  • @tsaukpaetra said in Java quiz:

    Edit: Though to be fair, the last answer is literally inane...

    The loop will not be executed, the initial value of x > 5.

    Well, it is a good way to make sure the people taking the test are paying attention, isn't it?

    Also, a lot of novices do misunderstand the different between if() and while() in exactly this way, and while most quickly overcome the stupidity of this, it is the sort of thing which can easily re-emerge under the pressure of a test.

    On an unrelated note, I wonder what you think of something I said in the main site comments:

    Java was basically a bunch of Lisp hackers being asked to re-design Smalltalk to use C-style brace syntax and strong explicit typing, while looking as much like C++ as possible, all for a language meant to be used in embedded systems. The contradictions in this pretty much ensured a lot of WTFery in the language.

    Tobey Faire, most people forget about the whole "make it easier for hardware designers to use different microcontrollers by having a portable language optimized for embedded processing" thing, mainly because it got punted once the whole "let's demonstrate how powerful this is by making it embed in web browser" brainworm took hold at Sun.

    (It is my understanding that this idea, which led to the whole 'applet' thing, was originally floated before Mosaic was released, around 1992 or so, at a time when the WWW was seen as sort of a silly hack that was sort of nice as a way of sharing research papers; the idea was that if even something as weird and underpowered as the original httpd server could launch it on the text-based www client regardless of how powerful the client machine was, it had to be uber-portable.)



  • 0_1507735425364_9c97d8d9-73a1-4ec2-ba3e-623426e4f70d-image.png

    Looks like C# works the same way, to no one's surprise.

    EDIT: Though I think the default behavior on overflow may be configurable.

    EDIT2: I was correct! (Though not changing the default I guess, which makes sense)

    int x = 10;
    do
    {
    	x = checked(x * 20);
    }
    while (x > 5);
    Console.WriteLine(x);
    

    EDIT3: Apparently it can be applied through (), {}, or as a project-level setting!



  • @scholrlea I may need to retract part of what I said, though I would like to hear what others know or have heard about it first.

    According to Wicked-Pedo, the original impetus and use case for Java was the 'interactive television' brainfart that was popular in the late 1980s and early 1990s. If this is the case, then I doubt that they would have seen text-based Web pages as a suitable demonstrator technology. I am not sure where I got that idea, but I am guessing that I misunderstood something somewhere.

    OTOH, it would have led naturally to the Java ME idea, since different cable companies would have had their own special ❄ set top units, and would want to be able to use different models using different processors as the micro-controller prices fluctuated. Allowing them to do that without rewriting the whole system for each model would be a big selling point for a language.


  • Impossible Mission Players - A

    @scholrlea said in Java quiz:

    On an unrelated note, I wonder what you think of something I said in the main site comments:

    Java was basically a bunch of Lisp hackers being asked to re-design Smalltalk to use C-style brace syntax and strong explicit typing, while looking as much like C++ as possible, all for a language meant to be used in embedded systems. The contradictions in this pretty much ensured a lot of WTFery in the language.

    Sounds reasonable, but I wouldn't take the time to validate it.



  • @scholrlea said in Java quiz:

    On an unrelated note, I wonder what you think of something I said in the main site comments:

    Java was basically a bunch of Lisp hackers being asked to re-design Smalltalk to use C-style brace syntax and strong explicit typing, while looking as much like C++ as possible, all for a language meant to be used in embedded systems. The contradictions in this pretty much ensured a lot of WTFery in the language.

    Tobey Faire, most people forget about the whole "make it easier for hardware designers to use different microcontrollers by having a portable language optimized for embedded processing" thing, mainly because it got punted once the whole "let's demonstrate how powerful this is by making it embed in web browser" brainworm took hold at Sun.

    Funny thing is that if Java is not suitable for something, it is embedded development. Because in embedded development you:

    • have limited amount of memory, which is at odds with the separate-allocation-for-everything approach, and
    • often have processing deadlines, which is at odds with garbage collector¹, at least in any incarnation of it in any JVM so far².

    ¹Termitological note: garbage collection in the strict sense means a tracing collection and does not include reference counting, which, while slower, does not generally incur significant unpredictable pauses.

    ²It is actually possible to design a tracing garbage collector that runs in a thread in parallel to the mutator and just interleaves the tracing and collection phases there, but I don't think it's been implemented in JVM.



  • @bulb I think I heard a suggestion decades ago that there was an intention to be able to implement a JVM in hardware?



  • @pleegwat said in Java quiz:

    @bulb I think I heard a suggestion decades ago that there was an intention to be able to implement a JVM in hardware?

    Well, there is the Jazelle ARM extension. However that only implements the basic instructions; the memory manager, which is the most demanding thing for embedded systems still has to be implemented by the operating system. And it's not like the extension would bring that much compared to transpilation to the native instructions, either just-in-time or ahead-of-time (which is what Android eventually settled on!)


  • Impossible Mission - B

    @ben_warre said in Java quiz:

    Is there ever a good reason to allow int overflow?

    I've found a legitimate use for it once, when unpacking BER-compressed integers. It's not only allowable; it's necessary in the case of negative numbers.


  • Impossible Mission - B

    @scholrlea said in Java quiz:

    On an unrelated note, I wonder what you think of something I said in the main site comments:
    Java was basically a bunch of Lisp hackers being asked to re-design Smalltalk to use C-style brace syntax and strong explicit typing, while looking as much like C++ as possible, all for a language meant to be used in embedded systems. The contradictions in this pretty much ensured a lot of WTFery in the language.

    I think that's rather strange, considering the way its semantics ended up looking nothing at all like Lisp and Smalltalk and everything like another branch of the Simula and C families.



  • Reading comprehension is hard, let's go shopping!

    0_1507792676810_4c460921-e29f-4691-a760-0fa078df327c-image.png



  • @bulb said in Java quiz:

    The former would require giving a warning (“x > 5” is always true in ….).

    There's no requirement for such a warning.



  • @boomzilla said in Java quiz:

    @pie_flavor said in Java quiz:

    Rust does this right. In debug mode, it panics at integer overflow; in release mode (optimized), it overflows properly with two's complement. And you can use stdlib functions to do math in all 3 methods regardless of mode.

    Because nothing says :doing_it_right: like defined different behavior in debug and release builds!

    C# does it right in that the overflow checking is controlled by a separate compilation flag. Tyimg it to debug/release configuration is a can of worms - your CI builds will generally use release configuration, so you can write code which fails unit tests on the local machine, but passes them with flying colors on the build server...



  • @maciejasjmj said in Java quiz:

    C# does it right in that the overflow checking is controlled by a separate compilation flag.

    The right thing is letting you explicitly decide in code, not externally: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/checked

    TL;DR: checked and unchecked keywords. The compilation flag decides the default behaviour.



  • @zecc said in Java quiz:

    @maciejasjmj said in Java quiz:

    C# does it right in that the overflow checking is controlled by a separate compilation flag.

    The right thing would be letting you explicitly decide in code, not externally.

    You can override the default setting where needed, but in general you'll either want to have most operations checked with a few exceptions, or most operations unchecked with a few exceptions.



  • @maciejasjmj I've amended my post above, but not quickly enough it seems.



  • @ben_warre said in Java quiz:

    Is there ever a good reason to allow int overflow?

    Well, I asked that a short time ago on SO:

    We do still live in an era where such stupid "performance" question overrule rational hardware and language design. WTF.


  • Discourse touched me in a no-no place

    @homobalkanus said in Java quiz:

    So instead of replacing programmers we replace hiring managers?

    Don't need Deep Learning AI for that. A small shell script will do.


  • Discourse touched me in a no-no place

    @berniethebernie said in Java quiz:

    We do still live in an era where such stupid "performance" question overrule rational hardware and language design.

    It's usually insanely difficult to prove that wrapping overflow won't happen. There are exceptions to this, but they usually depend on knowing quite a bit about the higher-level semantics of how the arithmetic operations are being used — for example, it's pretty reasonable to assert that a reference count will never wrap — but you usually can't detect that automatically at the individual operation level except in the trivial case when all the arguments are constants.

    That means that once you switch to a non-wrapping strategy for handling overflow, you're either stuck with using saturating arithmetic (which is a useful thing in some cases, approximately equivalent to pegging the meter needle ;)) or you're having vast numbers of operations become ones that can throw exceptions. Saturating arithmetic is relatively well known in system control software, but is uncommon elsewhere (and requires quite different code to use safely compared to what you're used to) and making everything throw exceptions really stuffs over a lot of optimisations (exceptions are awfully expensive).

    So compiler authors dealing with producing code that runs at very high speed are quite reluctant to go that route (as semantics changes and exceptions are both visible and, in the latter case, optimisation-inhibiting). Instead they prefer passing poison values around, representing values that are very problematic if needed, but which will be no problem at all if they can be proved (or asserted!) to never occur. And most of the time that works out for them just fine.


  • Impossible Mission - B

    @dkf said in Java quiz:

    Saturating arithmetic is relatively well known in system control software, but is uncommon elsewhere

    It's pretty standard for graphics. In OpenGL, for example, if you have a color property that can range from 0.0 to 1.0, and you add two values that add up to greater than 1.0, it will saturate to 1.0, which is almost certainly what you wanted anyway (certainly much more so than wraparound would have been!)



  • @dkf said in Java quiz:

    for example, it's pretty reasonable to assert that a reference count will never wrap

    I don't know. I think some of my code may wrap a 16-bit reference counter in the right (wrong) circumstances.



  • @dkf said in Java quiz:

    @homobalkanus said in Java quiz:

    So instead of replacing programmers we replace hiring managers?

    Don't need Deep Learning AI for that. A small shell script will do.

    I'm pretty sure most agencies did that a long time ago.



  • @dkf said in Java quiz:

    for example, it's pretty reasonable to assert that a reference count will never wrap

    [TRIGGER WARNING: CIVILIZED DISCOURSE]



  • @dkf said in Java quiz:

    @berniethebernie said in Java quiz:

    We do still live in an era where such stupid "performance" question overrule rational hardware and language design.

    It's usually insanely difficult to prove that wrapping overflow won't happen.

    It is far easier, however, to prove that an overflow (or underflow) could happen, and either automatically add runtime handling for them (whether through promotion to bignums, throwing an exception, saturating, or some programmable combination thereof), or, as my long-time foil over as OSDev, Brendan Trotter, prefers, flag them as an error to require a manual intervention that would amount to the same things.

    While Brendan argues that this is not the same thing as compiler-generated overflow checking, it does have the same result, practically speaking, it just gives the client-programmer more detailed control over the response (but at the cost of requiring them to add it themselves). He does make the point that his approach means that in most instances, throwing an exception could be avoided - the situation can often be addressed directly instead, depending on what the code is doing. He also states that while he intends to support bignums of a certain size (up to 256 bits), his language would require all user-defined numeric types to specify their boundaries.

    My own solution - as described in that same thread - is to default to a system type that would automatically promote (which is pretty much the SOP for Lisp family languages, Python, Ruby, and most FP languages) but allow ranged types to define other behaviors in addition to this. This code is mean to give an example of how it would work, though I would still need to work out the actual approach I will end up using:

    (def Foo-Range 
      (constrainted-type Integer
        (cond (((<  $_ -250) (set! @_ -250))
               ((>= @_ 750) (raise overflow-exception! ~_))
               (else (set! @_ $_)))))
    

    This would tell the compiler which checks, if any, to insert when a variable of this type is assigned or mutated (the compiler is free to try and apply those checks at compile time, but if it can't make a clear determination, it would insert them into the runtime code). I added that I would probably have this wrapped in a macro to simplify the most common cases, which would generate the above from something like:

    (def Foo-Range
      (ranged-type Integer
                   :bounds (-250 750)
                   :underflow #saturate
                   :overflow #except overflow-exception!))
    

    In both cases, the compiler can select a base size - whether one of the system sizes, or the standard bignum models - that the ranged type could fit within. Well, that's the goal, anyway.

    IMAO, the real problem is that so many languages simply go Ostrich mode (leaving the programmers to the mercy of nasal demonsundefined behavior) on the topic out of an insistence that the only appropriate integer ranges and behaviors are those defined in hardware, based on the desire to avoid all runtime bounds checking. This also forces the client-programmer to add them manually, but without requiring them as Brendan intends his language to do - and worse, without even warning about them, so the potential problem is usually overlooked.

    Even something as simple as the standard requiring the compiler to offer an optional warning that the programmer might want to add a bounds check (e.g., something like -Wunchecked-bounds) would help, but it goes against the grain for C and C++ it seems. Perhaps someone more familiar with the newest standards could tell me if this has been addressed now or not.


  • Discourse touched me in a no-no place

    @pleegwat said in Java quiz:

    a 16-bit reference counter



  • @dkf said in Java quiz:

    @pleegwat said in Java quiz:

    a 16-bit reference counter

    You didn't specify a width, just 'a reference counter'. I'm not sure from the back of my head if the code in question uses 32-bit or 64-bit reference counters, but I know they're not 16-bit ones.

    I don't doubt there are situations which will overflow a 32-bit counter, and you can probably even arrange a 64-bit refcount to overflow. Though I hazard if that's a probable risk on your application there are other design concepts to reconsider.


Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.