Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question How to make random float with min max exclusive?

Discussion in 'Scripting' started by tzxbbo, Aug 19, 2023.

  1. tzxbbo

    tzxbbo

    Joined:
    Dec 14, 2019
    Posts:
    57
    Something like this
    Random.Range(0f, 1f)

    But I don't want to get a 1f at any chance

    Could this work?
    Random.Range(0f, 1f - Mathf.Epsilon)


    but given the rules of Epislon
    • anyValue + Epsilon = anyValue
    • anyValue - Epsilon = anyValue
    • 0 + Epsilon = Epsilon
    • 0 - Epsilon = -Epsilon
    it seems 1f - Epsilon = 1f which makes my solution not working ??


    If there's not other way I will simply just reroll when getting 1f as a result, but it feels not as elegant
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Well what's the actual maximum value that you want to allow? Just use that as your second parameter.
     
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    Years ago when I wrote a wrapper for unityengine.Random to give it object identity and implement a generalized 'IRandom' interface I just picked arbitrary values of 0.9999...:
    https://github.com/lordofduct/space...ppy.core/Runtime/src/Utils/RandomUtil.cs#L306

    For float I picked 0.99999f; (5 sig values)
    For double I picked 0.99999999d; (8 sig values)

    Technically speaking you can easily fit 7 sig values into the float, so you could expand that out to 0.9999999f. I have noticed if you give it an 8th digit, it'll get rounded to 1f (and actually rounded, it'll equate to 1).

    For double, you may notice in that link above my "SimplePCG" random number generator does this:
    Code (csharp):
    1. return (double)this.GetNext() / (double)(0x100000000u);
    https://github.com/lordofduct/space...ppy.core/Runtime/src/Utils/RandomUtil.cs#L548

    That 0x100000000u is basically uint.MaxValue + 1. Since 'GetNext' returns a uint I'm basically saying give me a number where all lower 64-bits represent a fractional value between 0->1 exclusive of 1.

    Of course... looking at this now for my 'float' version I just cast that to a float... and huh... I never realized this but that's BAD. I shouldn't have done that. Casting that to a float will round it to 1f (like the 0.99999999f above). I wrote this a decade ago though... so, not surprised I messed that up. I'm going to go fix that.

    ...

    Regardless. I wouldn't be too concerned honestly. Just set it to 0.99999... up to the number of sig values you want, but short of the max sig values supported.
     
  4. tzxbbo

    tzxbbo

    Joined:
    Dec 14, 2019
    Posts:
    57
    anything but not 1f, I'll probably just do a 1f - 0.00001f, cause it doesn't seem there's a good solution
     
  5. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    I mean a good solution is the one that works. You only have so many points of precision with float, so 'just less than 1f' is not unreasonable to just hard-code.

    Probably best to have a
    const
    value somewhere to make this easier to reuse as well.
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    I changed the code in my linked source code. I now use these and fixed the next float methods.

    Code (csharp):
    1.         const int MAX_SINGLE_NUMERATOR = 0x7FFFFF;
    2.         const int MAX_SINGLE_DENOMINATOR = 0x800000;
    3.         const float MAX_SINGLE_RATIO = (float)MAX_SINGLE_NUMERATOR / (float)MAX_SINGLE_DENOMINATOR;
    4.         const ulong MAX_DOUBLE_NUMERATOR = 0x1FFFFFFFFFFFFFu;
    5.         const ulong MAX_DOUBLE_DENOMINATOR = 0x20000000000000u;
    6.         const double MAX_DOUBLE_RATIO = MAX_DOUBLE_NUMERATOR / MAX_DOUBLE_DENOMINATOR;
    These are technically the largest possible values < 1 that can be stored in your floats.
    0x7FFFFF = 23 digits of 1 in binary
    0x1FFFFFFFFFFFFF = 53 digits of 1 in binary

    Which those are the sig value ranges of float and double respectively.
    And the denominators are just those values + 1.
     
    Last edited: Aug 19, 2023
    spiney199 likes this.
  7. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    If you don't like setting your max to a similar 0.999999, then the only other solutions is either:
    Code (CSharp):
    1. int maxValue = 10000;
    2. int random = Random.Range(0,maxValue); // exclusive
    3. float rand = random / maxValue;
    or
    Code (CSharp):
    1. if (randomValue > 0.5f) randomValue -= 0.00001f;
    But in all honesty I don't see anything wrong with just saying:
    Code (CSharp):
    1. float maxValue = 0.99999999f;
    2. float random = Random.Range(0, maxValue);