Value | Name | IsNaN |
IsFinite |
IsInfinity |
IsNegativeInfinity |
IsPositiveInfinity |
== 0 |
IsNegativeZero |
IsPositiveZero |
IsNegative |
< 0f |
< -0f |
<= 0f |
<= -0f |
IsNormal |
IsSubnormal |
|||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 |
PositiveZero |
- | true | - | - | - | true | - | true | - | - | - | true | true | - | - | |||||
-0 |
NegativeZero |
- | true | - | - | - | true | true | - | true | - | - | true | true | - | - | |||||
1 |
Unity |
- | true | - | - | - | - | - | - | - | - | - | - | - | true | - | |||||
-1 |
-Unity |
- | true | - | - | - | - | - | - | true | true | true | true | true | true | - | |||||
1E-45 |
Epsilon |
- | true | - | - | - | - | - | - | - | - | - | - | - | - | true | |||||
3.4028235E+38 |
MaxValue |
- | true | - | - | - | - | - | - | - | - | - | - | - | true | - | |||||
-3.4028235E+38 |
MinValue |
- | true | - | - | - | - | - | - | true | true | true | true | true | true | - | |||||
NaN |
NaN |
true | - | - | - | - | - | - | - | true | - | - | - | - | - | - | |||||
-Infinity |
NegativeInfinity |
- | - | true | true | - | - | - | - | true | true | true | true | true | - | - | |||||
Infinity |
PositiveInfinity |
- | - | true | - | true | - | - | - | - | - | - | - | - | - | - |
Last active
April 14, 2022 15:39
-
-
Save daiplusplus/ebb18d5a6804287544f397f300f7543b to your computer and use it in GitHub Desktop.
Generates a table that compares the output of .NET's IEEE-754 methods
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Instructions: copy-and-paste this into Linqpad 6 or later (I used Linqpad 7). | |
void Main() | |
{ | |
List<TestResult> results = Run( includeArithmeticResults: false ).ToList().Dump(); | |
String markdownTable = GenerateMarkdownTable( results ); | |
Util.WithStyle( data: markdownTable, htmlStyle: "font-family: monospace;" ).Dump(); | |
} | |
public static IEnumerable<TestResult> Run( Boolean includeArithmeticResults ) | |
{ | |
// Example literals: | |
yield return Test( 0f , "PositiveZero" ); | |
yield return Test( -0f , "NegativeZero" ); | |
yield return Test( 1f , "Unity" ); | |
yield return Test( -1f , "-Unity" ); | |
// yield return Test( 0.1f , "0.1f" ); | |
// yield return Test( 0.01f , "0.01f" ); | |
// yield return Test( 0.001f , "0.001f" ); // <-- This *isn't* subnormal... why not? | |
// Named constants: | |
// yield return Test( default(Single) , "default(Single)" ); | |
yield return Test( Single.Epsilon , nameof(Single.Epsilon) ); | |
yield return Test( Single.MaxValue , nameof(Single.MaxValue) ); | |
yield return Test( Single.MinValue , nameof(Single.MinValue) ); | |
yield return Test( Single.NaN , nameof(Single.NaN) ); | |
yield return Test( Single.NegativeInfinity, nameof(Single.NegativeInfinity) ); | |
yield return Test( Single.PositiveInfinity, nameof(Single.PositiveInfinity) ); | |
if( includeArithmeticResults ) | |
{ | |
// Arithmetic results: | |
yield return Test( 1f / 0f, "1/0" ); | |
// yield return Test( 1f / GetBig(), "1/99999999999999999999999999999999999999999f" ); | |
} | |
} | |
private static Single GetBig() // <-- to prevent compile-time const calculations. | |
{ | |
return DateTime.UtcNow.Year >= 2022 ? ( Single.MaxValue - 1 ) : 1; | |
} | |
public static class Ieee754 | |
{ | |
public static readonly Single Single_PositiveZero = 0f; | |
public static readonly Single Single_NegativeZero = -0f; | |
public static Boolean Single_IsZero( Single value ) => value == 0f; | |
public static Boolean Double_IsZero( Double value ) => value == 0d; | |
// https://stackoverflow.com/questions/4739795/how-can-i-test-for-negative-zero | |
private static readonly Int32 Single_NegativeZeroBits = BitConverter.SingleToInt32Bits( -0.0f ); | |
private static readonly Int64 Double_NegativeZeroBits = BitConverter.DoubleToInt64Bits( -0.0d ); | |
public static Boolean Single_IsNegativeZero( Single value ) => BitConverter.SingleToInt32Bits( value ) == Single_NegativeZeroBits; | |
public static Boolean Double_IsNegativeZero( Double value ) => BitConverter.DoubleToInt64Bits( value ) == Double_NegativeZeroBits; | |
public static Boolean Single_IsPositiveZero( Single value ) => BitConverter.SingleToInt32Bits( value ) == default(Single); | |
public static Boolean Double_IsPositiveZero( Double value ) => BitConverter.DoubleToInt64Bits( value ) == default(Double); | |
} | |
public static TestResult Test( Single value, String name ) | |
{ | |
return new TestResult( value, name ) | |
{ | |
IsNaN = Single.IsNaN( value ), | |
IsNegativeInfinity = Single.IsNegativeInfinity( value ), | |
IsPositiveInfinity = Single.IsPositiveInfinity( value ), | |
IsInfinity = Single.IsInfinity( value ), // <-- Placing this property next to | |
#if !LINQPAD5 | |
IsFinite = Single.IsFinite( value ), | |
IsNegative = Single.IsNegative( value ), | |
IsNormal = Single.IsNormal( value ), | |
IsSubnormal = Single.IsSubnormal( value ), | |
#endif | |
IsZero = Ieee754.Single_IsZero( value ), | |
IsNegativeZero = Ieee754.Single_IsNegativeZero( value ), | |
IsPositiveZero = Ieee754.Single_IsPositiveZero( value ), | |
IsLtPosZero = value < Ieee754.Single_PositiveZero, | |
IsLtePosZero = value <= Ieee754.Single_PositiveZero, | |
IsLtNegZero = value < Ieee754.Single_NegativeZero, | |
IsLteNegZero = value <= Ieee754.Single_NegativeZero | |
}; | |
} | |
public class TestResult | |
{ | |
public TestResult( Single value, String name ) | |
{ | |
this.Value = value; | |
this.Name = name; | |
} | |
public readonly Single Value; | |
public readonly String Name; | |
public readonly String _1 = " "; | |
public /*readonly*/ Boolean? IsNaN; // .NET Fx 1.0+ | |
public /*readonly*/ Boolean? IsFinite; // .NET Core 2.1+ | |
public /*readonly*/ Boolean? IsInfinity; // .NET Fx 1.0+ | |
public readonly String _2 = " "; | |
public /*readonly*/ Boolean? IsNegativeInfinity; // .NET Fx 1.0+ | |
public /*readonly*/ Boolean? IsPositiveInfinity; // .NET Fx 1.0+ | |
public readonly String _3 = " "; | |
public /*readonly*/ Boolean? IsZero; // `==` operator | |
public /*readonly*/ Boolean? IsNegativeZero; // BitConverter | |
public /*readonly*/ Boolean? IsPositiveZero; // BitConverter | |
public readonly String _4 = " "; | |
public /*readonly*/ Boolean? IsNegative; // .NET Core 2.1+ | |
// These properties are intended to compare `value < 0f` to using `Single.IsNegative` and if things are different for NegativeZero vs. PositiveZero. | |
// UPDATE: The `<=` comparisons match, but I didn't expect `( -0f < 0f ) == false`. | |
public /*readonly*/ Boolean? IsLtPosZero; // `< 0f` operator | |
public /*readonly*/ Boolean? IsLtNegZero; // `< -0f` operator | |
public /*readonly*/ Boolean? IsLtePosZero; // `<= 0f` operator | |
public /*readonly*/ Boolean? IsLteNegZero; // `<= -0f` operator | |
public readonly String _5 = " "; | |
public /*readonly*/ Boolean? IsNormal; // .NET Core 2.1+ | |
public /*readonly*/ Boolean? IsSubnormal; // .NET Core 2.1+ | |
} | |
public static String GenerateMarkdownTable( List<TestResult> results ) | |
{ | |
// An exercise for the reader. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Visual form of the table (more compact, shows true vs. false with more contrast too):
I'm surprised that negative-zero is not considered less-than positive-zero, weird.