MathF.FusedMultiplyAdd incorrectly rounds subnormal results on musl's software fallback
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
The inputs and underlying musl bug are described in [this article](https://shnatsel.github.io/implementing-fma-finding-bugs-in-std/)
### Description
`MathF.FusedMultiplyAdd` incorrectly rounds some subnormal results when its software fallback calls musl's `fmaf`. The same inputs produce different results with and without hardware FMA.
### Reproduction Steps
Create a .NET 10 console app on musl-based Linux, such as Alpine:
```sh
dotnet new console -f net10.0
```
Replace `Program.cs` with:
```csharp
float x = BitConverter.UInt32BitsToSingle(0x97000800);
float y = BitConverter.UInt32BitsToSingle(0x1cfff001);
float z = BitConverter.UInt32BitsToSingle(0x00010002);
Console.WriteLine($"0x{BitConverter.SingleToUInt32Bits(MathF.FusedMultiplyAdd(x, y, z)):x8}");
```
Force the software fallback:
```sh
DOTNET_EnableHWIntrinsic=0 DOTNET_ReadyToRun=0 dotnet run -c Release
```
### Expected behavior
```text
0x00010001
```
This is the correctly rounded result and is also returned with hardware FMA enabled.
### Actual behavior
```text
0x00010002
```
### Configuration
Reproduced with .NET 10.0.11 (`linux-musl-x64`) and musl 1.2.5-r12 from Alpine 3.22, using the default round-to-nearest, ties-to-even mode.
### Other information
[CoreCLR's fallback delegates to `fmaf`](https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/floatsingle.cpp#L162-L166). musl's halfway-case check does not account for subnormal results, leading to double rounding. `Vector128.FusedMultiplyAdd` also reproduces the incorrect result through its scalar fallback.
Contributor guide
Research direction
Start with the fallback delegation in src/coreclr/vm/floatsingle.cpp around the linked fmaf call, then reproduce the output using the provided Program.cs on Alpine or another musl-based Linux environment with the listed environment variables. Done means the software fallback returns 0x00010001 for the supplied inputs, matching hardware FMA, including the Vector128.FusedMultiplyAdd scalar fallback.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, linux
- Domain
- operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100