-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Description
ILDasm (9.0) outputs the wrong format in float64(...)
and float32(...)
for double and float values without decimal places. The decimal point is missing.
ILAsm interprets these as int64
and therefore casts them to double
or float
values, which leads to incorrect values.
Reproduction Steps
- Compile the code to DLL
ildasm Program.dll /out=Program.il
ilasm Program.il /DLL
ildasm Program.dll /out=Program.il
(to see the effect)
public static class Program
{
public const double ValueDoubleOne = 1.0d;
public const double ValueDoubleOnePointTwo = 1.2d;
public const double ValueDoubleMinusOne = -1.0d;
public const double ValueDoubleMinusZero = -0.0d;
public const float ValueFloatOne = 1.0f;
public const float ValueFloatOnePointTwo = 1.2f;
public const float ValueFloatMinusOne = -1.0f;
public const float ValueFloatMinusZero = -0.0f;
}
or
.class public abstract auto ansi sealed beforefieldinit Program
extends [System.Runtime]System.Object
{
.field public static literal float64 ValueDoubleOne = float64(1.)
.field public static literal float64 ValueDoubleOnePointTwo = float64(1.2)
.field public static literal float64 ValueDoubleMinusOne = float64(-1.)
.field public static literal float64 ValueDoubleMinusZero = float64(-0.)
.field public static literal float32 ValueFloatOne = float32(1.)
.field public static literal float32 ValueFloatOnePointTwo = float32(1.2)
.field public static literal float32 ValueFloatMinusOne = float32(-1.)
.field public static literal float32 ValueFloatMinusZero = float32(-0.)
}
Expected behavior
Output should be the same as input:
.class public abstract auto ansi sealed beforefieldinit Program
extends [System.Runtime]System.Object
{
.field public static literal float64 ValueDoubleOne = float64(1.)
.field public static literal float64 ValueDoubleOnePointTwo = float64(1.2)
.field public static literal float64 ValueDoubleMinusOne = float64(-1.)
.field public static literal float64 ValueDoubleMinusZero = float64(-0.)
.field public static literal float32 ValueFloatOne = float32(1.)
.field public static literal float32 ValueFloatOnePointTwo = float32(1.2)
.field public static literal float32 ValueFloatMinusOne = float32(-1.)
.field public static literal float32 ValueFloatMinusZero = float32(-0.)
}
Actual behavior
The IL code from step 2 will be (note the missing .
s):
.class public abstract auto ansi sealed beforefieldinit Program
extends [System.Runtime]System.Object
{
.field public static literal float64 ValueDoubleOne = float64(1)
.field public static literal float64 ValueDoubleOnePointTwo = float64(1.2)
.field public static literal float64 ValueDoubleMinusOne = float64(-1)
.field public static literal float64 ValueDoubleMinusZero = float64(-0)
.field public static literal float32 ValueFloatOne = float32(1)
.field public static literal float32 ValueFloatOnePointTwo = float32(1.2)
.field public static literal float32 ValueFloatMinusOne = float32(-1)
.field public static literal float32 ValueFloatMinusZero = float32(-0)
}
After a roundtrip in step 4 the IL code will be:
.class public abstract auto ansi sealed beforefieldinit Program
extends [System.Runtime]System.Object
{
.field public static literal float64 ValueDoubleOne = float64(4.9406564584124654e-324)
.field public static literal float64 ValueDoubleOnePointTwo = float64(1.2)
.field public static literal float64 ValueDoubleMinusOne = float64(0xFFFFFFFFFFFFFFFF) // -nan
.field public static literal float64 ValueDoubleMinusZero = float64(0)
.field public static literal float32 ValueFloatOne = float32(1.4012985e-45)
.field public static literal float32 ValueFloatOnePointTwo = float32(1.2)
.field public static literal float32 ValueFloatMinusOne = float32(0xFFFFFFFF)
.field public static literal float32 ValueFloatMinusZero = float32(0)
}
Regression?
Yes, it is a regression, the following versions of ILDasm work:
- 4.0.30319.0
- 8.0.0
- 9.0.0-preview.3.24172.9
The first version that no longer works is: 9.0.0-preview.4.24266.19
I think the behavior was changed with this PR: #98336
Known Workarounds
As a workaround, you can only correct the output using appropriate regex.
[GeneratedRegex(@"float(32|64)\((-?\d+)\)")]
private static partial Regex FloatNumber { get; }
if (FloatNumber.IsMatch(ilcode))
{
ilcode = FloatNumber.Replace(ilcode, "float$1($2.)");
}
Configuration
- NET 9.0
- Windows 10 22H2 x64
- It should be independent of the configuration, as the code is used for all configurations. However, I have not tested it.
Other information
I am not an expert in C++, but I think the problem should be fixed by adding this code snippet after the two places?
runtime/src/coreclr/ildasm/dasm.cpp
Line 2611 in 163b59e
sprintf_s(szf, 32, "%.*g", 8, (double)MDDV.m_fltValue); |
runtime/src/coreclr/ildasm/dasm.cpp
Line 2625 in 163b59e
sprintf_s(szf, 32, "%.*g", 17, MDDV.m_dblValue); |
if (strchr(szf, '.') == NULL && !IsSpecialNumber(szf))
strcat_s(szf, 32, ".");
Although these places were also affected by the change, they do not seem to make any difference for ILAsm:
runtime/src/coreclr/ildasm/dasm.cpp
Line 1917 in 163b59e
sprintf_s(str, 64, "%.*g", 8, (double)(*((float*)dataPtr))); |
runtime/src/coreclr/ildasm/dasm.cpp
Line 1936 in 163b59e
sprintf_s(str, 64, "%.*g", 17, *((double*)dataPtr)); |