Rationale
The .NET framework does not currently provide scalar single-precision floating-point support for many of the trigonometric, logarithmic, and other common mathematical functions.
Single-precision floating-point support should be provided for these mathematical functions in order to better interop with high-performance, scientific, and multimedia-based applications where single-precision floating-points are the only implementation required and/or used.
Since adding these methods to the existing System.Math class would break backwards compatibility, they should be provided through a separate class that provides the same and/or similar functionality.
Providing these new APIs would:
- improve code readability (especially in the non-hardware backed implementations of methods in the
System.Numerics.Vector structs)
- improve code performance in areas where the single-precision implementation is all that is required
- provide better interoperability with high-performance, scientific, and multimedia-based applications where data structures and existing code may be dependent on single-precision implementations of such methods.
- provide a more advanced infrastructure for mathematics in the CoreCLR
Proposed API
The proposed API here provides feature-parity with the existing double-precision math functions provided by the framework.
System.BitConverter
public static class BitConverter
{
public static float Int32BitsToSingle(int);
public static int SingleToInt32Bits(float);
}
System.Single
public static partial class MathF
{
public const float E = 2.71828183f;
public const float PI = 3.14159265f;
// Trigonometric Functions
public static float Acos(float);
public static float Asin(float);
public static float Atan(float);
public static float Atan2(float, float);
public static float Cos(float);
public static float Sin(float);
public static float Tan(float);
// Hyperbolic Functions
public static float Cosh(float);
public static float Sinh(float);
public static float Tanh(float);
// Exponential Functions
public static float Exp(float);
// Logarithmic Functions
public static float Log(float);
public static float Log(float, float);
public static float Log10(float);
// Power Functions
public static float Pow(float, float);
public static float Sqrt(float);
// Rounding Functions
public static float Ceiling(float);
public static float Floor(float);
public static float Round(float);
public static float Round(float, int);
public static float Round(float, int, MidpointRounding);
public static float Round(float, MidpointRounding);
public static float Truncate(float);
// Manipulation Functions
public static int Sign(float);
// Minimum, Maximum, and Difference Functions
public static float Max(float, float);
public static float Min(float, float);
// Other Functions
public static float Abs(float);
public static float IEEERemainder(float, float);
}
Example Usage
Currently to calculate the Tangent for each member of the System.Numerics.Vector4 struct, you currently have to call the double-precision version of the method and cast to the result to a single-precision value:
public static Vector4 Acos(Vector4 value)
{
return new Vector4((float)(Math.Acos(value.X)),
(float)(Math.Acos(value.Y)),
(float)(Math.Acos(value.Z)),
(float)(Math.Acos(value.W)));
}
With the proposed changes, this would now be simplified to the following:
public static Vector4 Acos(Vector4 value)
{
return new Vector4(Mathf.Acos(value.X),
Mathf.Acos(value.Y),
Mathf.Acos(value.Z),
Mathf.Acos(value.W));
}
The System.Numerics library itself is filled with similar examples as are various bits of code in the CoreFX and CoreCLR repositories.
Perf Numbers
All performance tests are implemented as follows:
- 100,000 iterations are executed
- The time of all iterations are aggregated to compute the
Total Time
- The time of all iterations are averaged to compute the
Average Time
- A single iteration executes some simple operation, using the function under test, 5000 times
The execution time below is the Total Time for all 100,000 iterations, measured in seconds.
Hardware: Desktop w/ 3.7GHz Quad-Core A10-7850K (AMD) and 16GB RAM
| Function |
Improvment |
Execution Time - Double |
Execution Time - Single |
| Abs |
0.199243555% |
0.63752649s |
0.63625626s |
| Acos |
12.30220910% |
11.5265412s |
10.1085220s |
| Asin |
18.66801808% |
11.9472425s |
9.71692911s |
| Atan |
21.10350002% |
10.9964683s |
8.67582861s |
| Atan2 |
20.51327307% |
24.3328097s |
19.3413540s |
| Ceiling |
12.91487191% |
1.87116459s |
1.62950608s |
| Cos |
5.026665542% |
7.19916547s |
6.83728750s |
| Cosh |
16.46166555% |
13.5416170s |
11.3124413s |
| Exp |
33.67586387% |
6.65578424s |
4.41439140s |
| Floor |
10.39208688% |
1.74655247s |
1.56504922s |
| Log |
19.81117664% |
6.42244806s |
5.15008553s |
| Log10 |
18.40605725% |
6.75118866s |
5.50856101s |
| Pow |
47.85595440% |
31.8820155s |
16.6245727s |
| Round |
0.976398142% |
4.22620632s |
4.18494172s |
| Sin |
15.49539339% |
5.98022268s |
5.05356365s |
| Sinh |
17.96609899% |
14.6242270s |
11.9968239s |
| Sqrt |
4.676516651% |
2.51281945s |
2.39530703s |
| Tan |
30.33470555% |
9.07290178s |
6.32066374s |
| Tanh |
0.108182099% |
8.12724112s |
8.11844890s |
I believe some extra perf will be squeezed out when the intrinsics (such as CORINFO_INTRINSIC_Sqrt) are properly implemented in the VM layer for single-precision values. Without such functionality, it falls back to the double-precision functionality (extra precision, reduced performance) for certain calls.
Pull Request
There is a sample pull request covering these changes available: dotnet/coreclr#5492
Additional Details
This will require several changes in the CoreCLR as well to support the new APIs via FCALLs and Intrinsics.
Rationale
The .NET framework does not currently provide scalar single-precision floating-point support for many of the trigonometric, logarithmic, and other common mathematical functions.
Single-precision floating-point support should be provided for these mathematical functions in order to better interop with high-performance, scientific, and multimedia-based applications where single-precision floating-points are the only implementation required and/or used.
Since adding these methods to the existing
System.Mathclass would break backwards compatibility, they should be provided through a separate class that provides the same and/or similar functionality.Providing these new APIs would:
System.Numerics.Vectorstructs)Proposed API
The proposed API here provides feature-parity with the existing double-precision math functions provided by the framework.
System.BitConverter
System.Single
Example Usage
Currently to calculate the Tangent for each member of the
System.Numerics.Vector4struct, you currently have to call the double-precision version of the method and cast to the result to a single-precision value:With the proposed changes, this would now be simplified to the following:
The
System.Numericslibrary itself is filled with similar examples as are various bits of code in the CoreFX and CoreCLR repositories.Perf Numbers
All performance tests are implemented as follows:
Total TimeAverage TimeThe execution time below is the
Total Timefor all 100,000 iterations, measured in seconds.Hardware: Desktop w/ 3.7GHz Quad-Core A10-7850K (AMD) and 16GB RAM
I believe some extra perf will be squeezed out when the intrinsics (such as
CORINFO_INTRINSIC_Sqrt) are properly implemented in the VM layer for single-precision values. Without such functionality, it falls back to the double-precision functionality (extra precision, reduced performance) for certain calls.Pull Request
There is a sample pull request covering these changes available: dotnet/coreclr#5492
Additional Details
This will require several changes in the CoreCLR as well to support the new APIs via FCALLs and Intrinsics.