Why does the .Net implementation of Random.Next(int, int) require maxValue >= minValue?

Cadde

While making a method to place a newly created form on a completely random location on my screen without going outside the bounds of said screen i had an interesting code contracts message stating...

contract requires unproven minValue <= maxValue

... while using the code:

        Location = new Point(
            this.rnd.Next(targetBounds.Left, targetBounds.Right - Width),
            this.rnd.Next(targetBounds.Top, targetBounds.Bottom - Height));

Having looked at the .Net implementation i am baffled.

My question is, why couldn't they simply swap the arguments around when max value is < minvalue.
Is there any (beyond maybe not catching the obscure bug) reason as to why one shouldn't allow Random.Next(100, 50)?

It shouldn't make any difference if you say "give me a random number between 100 (exclusive) and 50 (inclusive)" and "... between 50 (inclusive) and 100 (exclusive)".
The range of numbers would still be the same wouldn't it?

And that is before one considers the pitfalls with Random.Next(1, 2) and Random.Next(1, 1), both of which are perfectly valid code without any warnings. Both return 1 on every new generation and actually should warn the user about this behavior.

EDIT An example of a potential pitfall when calling Random.Next(20, 20).

If you assume in your code that providing an upper bound of 20 will always give you random numbers between lower and 19 and your lower bound is allowed to go to 20 inclusive you, in this particular case, would potentially break your code when the lower bound hits 20 as that is the one case that Random.Next would return 20. Imagine you have some code with the upper bound in the millions, it might take some time before you suddenly run into that edge case. At least it IS documented.

I am already aware of this question: Why can minValue == maxValue in Random.Next?

paxdiablo

The answer is most likely "they could have, but they didn't".

Programming languages and libraries are full of quirks like this, except that what you consider a quirk, others may call a design decision :-)

It would make little sense to provide a function that took a minimum and maximum value which then proceeded to give you a value outside the scope of what you asked for. Fair enough if the parameters were something like limit1/limit2 or one_end/the_other_end but, in any situation where the very definition of the values are contained in the name (such as min/max or upper/lower), you're best off following certain guidelines, such as the principle of least astonishment.

All the behaviour you're discussing is well-documented, it's not like it's a bug (if the doco was wrong) or even gray (if the doco didn't mention it). It's plainly spelled out for all to read and take note of:

minValue: The inclusive lower bound of the random number returned.

maxValue: The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue.

Return Value: A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values includes minValue but not maxValue. If minValue equals maxValue, minValue is returned.

If you really want a reversible range, there's nothing stopping you from doing it yourself, with something like (pseudo-code):

def revRandom (end1, end2):
    if end1 > end2:
        return realRandom (end2, end1)
    return realRandom (end1, end2)

The only tricky bit is how you handle inclusive/exclusive at either end. In the example above, I've chosen to have the higher number exclusive no matter what. If you want to have the second parameter exclusive, you'll need a slight change.


In any case, based on your code, you seem to be wanting to place a corner of some object so that the entire object falls within the target bounds. If that's the case, the behaviour you desire is exactly the thing you want to avoid.

If the width of your object was for some reason greater than the width of the target bounds, it would place you object outside of the boundary (and, of course, if it's not greater, you have no problem anyway since the arguments to .Next() will be fine).

To illustrate (we'll only cover left/right boundaries here but top/bottom is similar), assume you target boundaries are left=100 and right=200. If the width of your object is anything between 1 and 100 (a), it will work fine, and the object will be fully contained.

If the object decides to fatten up a bit to 150 (for example), then the left side of it will fall somewhere between 50 and 100, well outside the permissible boundaries.


(a) There may be subtle edge cases at the boundary points caused by Jedi errors (Obi Wan = OB1 = off-by-one, get it?) but I'm discounting that for simplicity.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Why does (float.MaxValue / float.MinValue) evaluate to Infinity?

Why is Random.nextInt(int bound) specified in Random but not Random.nextInt(int origin, int bound)?

Confusing implementation of java.util.Random nextInt(int n)

Why is DateTime.MinValue a constant expression, but not DateTime.MaxValue in VB.NET?

Why does int.MinValue % -1 cause and OverflowException

Why does random.nextInt() create "IllegalArgumentException: n <= 0: -1"?

Why does Square(int.MaxValue) return 1?

Why does int32.maxvalue + 1 overflow a long?

Why DateTime.MinValue and DateTime.MaxValue are not const

Why does the multiplication implementation for Complex require a RealFloat?

Implementation of java.util.Random.nextInt

c# Random number between Double.MinValue and Double.MaxValue

Random.nextInt(int) is [slightly] biased

How to use Vuelidate minValue maxValue

Iterating from MinValue to MaxValue with overflow

Why does the shuffle' function require an Int parameter?

Why does my script require int()?

Why does Long.MaxValue fit in a Double but < Long.MaxValue not

Why does one interface implementation require a cast while another does not?

Math.random() versus Random.nextInt(int)

C# Random Seed Value of int.MinValue Unpredictable

Can Random.Next() ever return int.MaxValue?

Loop/Assign for MaxValue, MinValue and Calculate Interval

VBA Loop/Assign for MaxValue, MinValue and Calculate Interval

Why does declaring a variable as type int require casting a stream? long type does not require a cast

Why dividing int.MinValue by -1 threw OverflowException in unchecked context?

Why does ASP.NET 5 on Linux require kestrel?

Why does one random int become the same as another random int after I specifically prevent this?

Why does printf output random values for double and 0.000000 for int?