Reputation: 518
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?
Upvotes: 0
Views: 445
Reputation: 881663
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 tominValue
.Return Value: A 32-bit signed integer greater than or equal to
minValue
and less thanmaxValue
; that is, the range of return values includesminValue
but notmaxValue
. IfminValue
equalsmaxValue
,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.
Upvotes: 7
Reputation: 101700
I find this question to be rather bizarre.
My question is, why couldn't they simply swap the arguments around when max value is < minvalue.
The parameters are called mixValue
and manValue
. If you passed a value of 7
for minValue
and 3
for maxValue
, and it returned a value of 4, this would be completely shocking and undesirable behavior. There is no value greater than or equal to 7 and less than or equal to 3, so all the method can do is throw an exception.
They could have instead called the parameters oneValue
and otherValue
, and ensured that the result was between, but I don't think there's anything strange about having a method that takes specify a range of numerical values in increasing order; this is quite common. (And I don't think it's an unreasonable demand, either).
And in any case, two different inputs to the same method call resulting in the same output just doesn't sit right with me
Why? Math.Floor()
will produce 3
for the inputs 3.04
, 3.99997
, and several trillion other inputs. There is nothing wrong with a method producing the same output for different inputs. This, too, is quite a common situation.
Upvotes: 4