Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure that IsPow2 for floating-point numbers correctly take subnormal values into account #88839

Merged
merged 1 commit into from
Jul 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions src/libraries/System.Private.CoreLib/src/System/Double.cs
Original file line number Diff line number Diff line change
Expand Up @@ -533,12 +533,28 @@ public static bool IsPow2(double value)
{
ulong bits = BitConverter.DoubleToUInt64Bits(value);

ushort biasedExponent = ExtractBiasedExponentFromBits(bits);;
if ((long)bits <= 0)
{
// Zero and negative values cannot be powers of 2
return false;
}

ushort biasedExponent = ExtractBiasedExponentFromBits(bits);
ulong trailingSignificand = ExtractTrailingSignificandFromBits(bits);

return (value > 0)
&& (biasedExponent != MinBiasedExponent) && (biasedExponent != MaxBiasedExponent)
&& (trailingSignificand == MinTrailingSignificand);
if (biasedExponent == MinBiasedExponent)
{
// Subnormal values have 1 bit set when they're powers of 2
return ulong.PopCount(trailingSignificand) == 1;
}
else if (biasedExponent == MaxBiasedExponent)
{
// NaN and Infinite values cannot be powers of 2
return false;
}

// Normal values have 0 bits set when they're powers of 2
return trailingSignificand == MinTrailingSignificand;
}

/// <inheritdoc cref="IBinaryNumber{TSelf}.Log2(TSelf)" />
Expand Down
22 changes: 19 additions & 3 deletions src/libraries/System.Private.CoreLib/src/System/Half.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1194,12 +1194,28 @@ public static bool IsPow2(Half value)
{
ushort bits = BitConverter.HalfToUInt16Bits(value);

if ((short)bits <= 0)
{
// Zero and negative values cannot be powers of 2
return false;
}

byte biasedExponent = ExtractBiasedExponentFromBits(bits);
ushort trailingSignificand = ExtractTrailingSignificandFromBits(bits);

return (value > Zero)
&& (biasedExponent != MinBiasedExponent) && (biasedExponent != MaxBiasedExponent)
&& (trailingSignificand == MinTrailingSignificand);
if (biasedExponent == MinBiasedExponent)
{
// Subnormal values have 1 bit set when they're powers of 2
return ushort.PopCount(trailingSignificand) == 1;
}
else if (biasedExponent == MaxBiasedExponent)
{
// NaN and Infinite values cannot be powers of 2
return false;
}

// Normal values have 0 bits set when they're powers of 2
return trailingSignificand == MinTrailingSignificand;
}

/// <inheritdoc cref="IBinaryNumber{TSelf}.Log2(TSelf)" />
Expand Down
22 changes: 19 additions & 3 deletions src/libraries/System.Private.CoreLib/src/System/Single.cs
Original file line number Diff line number Diff line change
Expand Up @@ -529,12 +529,28 @@ public static bool IsPow2(float value)
{
uint bits = BitConverter.SingleToUInt32Bits(value);

if ((int)bits <= 0)
{
// Zero and negative values cannot be powers of 2
return false;
}

byte biasedExponent = ExtractBiasedExponentFromBits(bits);
uint trailingSignificand = ExtractTrailingSignificandFromBits(bits);

return (value > 0)
&& (biasedExponent != MinBiasedExponent) && (biasedExponent != MaxBiasedExponent)
&& (trailingSignificand == MinTrailingSignificand);
if (biasedExponent == MinBiasedExponent)
{
// Subnormal values have 1 bit set when they're powers of 2
return uint.PopCount(trailingSignificand) == 1;
}
else if (biasedExponent == MaxBiasedExponent)
{
// NaN and Infinite values cannot be powers of 2
return false;
}

// Normal values have 0 bits set when they're powers of 2
return trailingSignificand == MinTrailingSignificand;
}

/// <inheritdoc cref="IBinaryNumber{TSelf}.Log2(TSelf)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ public static void IsPow2Test()
Assert.False(BinaryNumberHelper<NFloat>.IsPow2(NegativeZero));
Assert.False(BinaryNumberHelper<NFloat>.IsPow2(NFloat.NaN));
Assert.False(BinaryNumberHelper<NFloat>.IsPow2(Zero));
Assert.False(BinaryNumberHelper<NFloat>.IsPow2(NFloat.Epsilon));
Assert.True(BinaryNumberHelper<NFloat>.IsPow2(NFloat.Epsilon));
Assert.False(BinaryNumberHelper<NFloat>.IsPow2(MaxSubnormal));
Assert.True(BinaryNumberHelper<NFloat>.IsPow2(MinNormal));
Assert.True(BinaryNumberHelper<NFloat>.IsPow2(One));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public static void IsPow2Test()
Assert.False(BinaryNumberHelper<double>.IsPow2(-0.0));
Assert.False(BinaryNumberHelper<double>.IsPow2(double.NaN));
Assert.False(BinaryNumberHelper<double>.IsPow2(0.0));
Assert.False(BinaryNumberHelper<double>.IsPow2(double.Epsilon));
Assert.True(BinaryNumberHelper<double>.IsPow2(double.Epsilon));
Assert.False(BinaryNumberHelper<double>.IsPow2(MaxSubnormal));
Assert.True(BinaryNumberHelper<double>.IsPow2(MinNormal));
Assert.True(BinaryNumberHelper<double>.IsPow2(1.0));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public static void IsPow2Test()
Assert.False(BinaryNumberHelper<Half>.IsPow2(NegativeZero));
Assert.False(BinaryNumberHelper<Half>.IsPow2(Half.NaN));
Assert.False(BinaryNumberHelper<Half>.IsPow2(Zero));
Assert.False(BinaryNumberHelper<Half>.IsPow2(Half.Epsilon));
Assert.True(BinaryNumberHelper<Half>.IsPow2(Half.Epsilon));
Assert.False(BinaryNumberHelper<Half>.IsPow2(MaxSubnormal));
Assert.True(BinaryNumberHelper<Half>.IsPow2(MinNormal));
Assert.True(BinaryNumberHelper<Half>.IsPow2(One));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public static void IsPow2Test()
Assert.False(BinaryNumberHelper<float>.IsPow2(-0.0f));
Assert.False(BinaryNumberHelper<float>.IsPow2(float.NaN));
Assert.False(BinaryNumberHelper<float>.IsPow2(0.0f));
Assert.False(BinaryNumberHelper<float>.IsPow2(float.Epsilon));
Assert.True(BinaryNumberHelper<float>.IsPow2(float.Epsilon));
Assert.False(BinaryNumberHelper<float>.IsPow2(MaxSubnormal));
Assert.True(BinaryNumberHelper<float>.IsPow2(MinNormal));
Assert.True(BinaryNumberHelper<float>.IsPow2(1.0f));
Expand Down