Skip to content

Commit

Permalink
Fix: NextFloat/Double could return a value equal to max due to floati…
Browse files Browse the repository at this point in the history
…ng point error
  • Loading branch information
AnnulusGames committed Aug 20, 2024
1 parent 511889a commit d71d0ab
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 2 deletions.
29 changes: 27 additions & 2 deletions src/RandomExtensions/RandomEx.Next.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,19 @@ public static float NextFloat(this IRandom random, float max)
public static float NextFloat(this IRandom random, float min, float max)
{
ThrowHelper.CheckMinMax(min, max);
return NextFloat(random) * (max - min) + min;

var r = NextFloat(random) * (max - min) + min;

if (r >= max)
{
#if !NET6_0_OR_GREATER
r = MathEx.BitDecrement(max);
#else
r = MathF.BitDecrement(max);
#endif
}

return r;
}

/// <summary>
Expand Down Expand Up @@ -206,7 +218,20 @@ public static double NextDouble(this IRandom random, double max)
public static double NextDouble(this IRandom random, double min, double max)
{
ThrowHelper.CheckMinMax(min, max);
return NextDouble(random) * (max - min) + min;

var r = NextDouble(random) * (max - min) + min;

// correct for rounding
if (r >= max)
{
#if !NET6_0_OR_GREATER
r = MathEx.BitDecrement(max);
#else
r = Math.BitDecrement(max);
#endif
}

return r;
}

/// <summary>
Expand Down
57 changes: 57 additions & 0 deletions src/RandomExtensions/Shims/MathEx.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
namespace RandomExtensions;

internal static class MathEx
{
#if !NET6_0_OR_GREATER
public static double BitDecrement(double x)
{
long bits = BitConverter.DoubleToInt64Bits(x);

if (((bits >> 32) & 0x7FF00000) >= 0x7FF00000)
{
// NaN returns NaN
// -Infinity returns -Infinity
// +Infinity returns double.MaxValue
return (bits == 0x7FF00000_00000000) ? double.MaxValue : x;
}

if (bits == 0x00000000_00000000)
{
// +0.0 returns -double.Epsilon
return -double.Epsilon;
}

// Negative values need to be incremented
// Positive values need to be decremented

bits += ((bits < 0) ? +1 : -1);
return BitConverter.Int64BitsToDouble(bits);
}

public static float BitDecrement(float x)
{
int bits = BitConverter.SingleToInt32Bits(x);

if ((bits & 0x7F800000) >= 0x7F800000)
{
// NaN returns NaN
// -Infinity returns -Infinity
// +Infinity returns float.MaxValue
return (bits == 0x7F800000) ? float.MaxValue : x;
}

if (bits == 0x00000000)
{
// +0.0 returns -float.Epsilon
return -float.Epsilon;
}

// Negative values need to be incremented
// Positive values need to be decremented

bits += ((bits < 0) ? +1 : -1);
return BitConverter.Int32BitsToSingle(bits);
}

#endif
}

0 comments on commit d71d0ab

Please sign in to comment.