Search Unity

Discussion Weird behavior when generating random floats/doubles

Discussion in 'Entity Component System' started by CookieStealer2, Aug 8, 2022.

  1. CookieStealer2

    CookieStealer2

    Joined:
    Jun 25, 2018
    Posts:
    119
    Consider this, here I generate a float in the interval [float.MinValue, float.MaxValue) and a double in the interval [double.MinValue, double.MaxValue):
    Code (CSharp):
    1. Unity.Mathematics.Random random = new Unity.Mathematics.Random(1);
    2.  
    3. float floatValue = random.NextFloat(float.MinValue, float.MaxValue);
    4. double doubleValue = random.NextDouble(double.MinValue, double.MaxValue);
    5.  
    6. UnityEngine.Debug.Log($"floatValue: {floatValue}");
    7. UnityEngine.Debug.Log($"doubleValue: {doubleValue}");
    It prints:
    The float value is OK, but why am I getting inf for the double?

    I tried tried running the code in a loop for 1000 iterations to see if it was because of a specific state of the generator. But no... I always get infinity for doubles and an actual value for the floats.

    Looking at the source code below it seems like I should get inf in both cases since the max - min will overflow.
    Code (CSharp):
    1.  
    2. [MethodImpl(MethodImplOptions.AggressiveInlining)]
    3. public float NextFloat(float min, float max) { return NextFloat() * (max - min) + min; }
    4.  
    5. [MethodImpl(MethodImplOptions.AggressiveInlining)]
    6. public double NextDouble(double min, double max) { return NextDouble() * (max - min) + min; }
    7.  
    Yet it magically works for floats. Anyone know what's going on here?
     
  2. Arnold_2013

    Arnold_2013

    Joined:
    Nov 24, 2013
    Posts:
    286
    with (float.MaxValue - float.MinValue) = (3.40282347E+38 - -3.40282347E+38) = 2 * float.MaxValue. I would imagine there would be a error since the value does not fit in a float.

    Does it work if you take a more reasonable value for min and max?
     
  3. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,975
    First thing in such cases that I check is the debugger. "Stringifying" floating point values can do all sorts of things to them (ie formatting, truncating) without you knowing about it.
     
  4. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,975
    Hmmm seems persistent, I get the same "Infinity" result:

    upload_2022-8-8_13-40-52.png

    But of course, this seems correct:

    Code (CSharp):
    1. public double NextDouble(double min, double max) { return NextDouble() * (max - min) + min; }
    Code (CSharp):
    1. var doubleMaxMinusMin = double.MaxValue - double.MinValue;
    2. => +Infinity
    Subtracting the min from max leads to infinity because it goes outside the scope of double values. In other words, essentially the same as:

    MaxValue - (-MaxValue)
    or:
    MaxValue + MaxValue

    I suppose floats "survive" this because in a 64-bit operating system they are propagated to double internally on the CPU.
     
    Last edited: Aug 8, 2022
  5. CookieStealer2

    CookieStealer2

    Joined:
    Jun 25, 2018
    Posts:
    119
    This is the information I was looking for. Thanks, that explains it.
    Still, it seems like a pretty weird behavior. Would have expected infinity in both cases.
     
  6. CookieStealer2

    CookieStealer2

    Joined:
    Jun 25, 2018
    Posts:
    119
    It works fine if (max - min) is within the scope of double values.
     
  7. CookieStealer2

    CookieStealer2

    Joined:
    Jun 25, 2018
    Posts:
    119
    Just made some tests about the auto propagation of floats to double. The circumstances for when it happens seems to be very specific.

    These floats do not propagate to doubles, so we get infinity:
    Code (CSharp):
    1.  
    2. float float1 = (float.MaxValue - float.MinValue) + float.MinValue; // Infinity
    3.  
    4. float float2 = FuncA(); // Infinity
    5.  
    6. public float FuncA()
    7. {
    8.     return (float.MaxValue - float.MinValue) + float.MinValue;
    9. }
    10.  
    But this one do and we get a valid float:
    Code (CSharp):
    1.  
    2. float float3 = FuncB(float.MinValue, float.MaxValue); // 3.40282347E+38
    3.  
    4. public float FuncB(float min, float max)
    5. {
    6.     return (max - min) + min;
    7. }
    8.  
    This is mind-boggling. I would expect all examples to behave the same.