寻求有关从 .NET Framework 4.8 到 .NET 8 扩展操作顺序的更改的解释

问题描述 投票:0回答:1

我们正在将应用程序从 .NET Framework 4.8 更新到 .NET 8。

在回归测试期间,我们注意到隐式加宽转换似乎以不同的顺序发生,导致结果发生一些变化。

在.NET Framework中,像这样的操作:

d1= f1 * f2
将在执行乘法之前将f1
f2
转换为双倍,而在.NET 8中将首先进行浮点数之间的乘法,并且那么就会发生扩大。
我知道浮点二进制数学的行为。我并不是试图声称其中之一是“错误的” - 更多只是试图理解为什么行为会改变。

并且:

有什么方法可以

暂时将 .NET 8 行为更改回 .NET Framework 行为,以便我们可以更好地理解我们的回归测试吗? (P.S.是的,我知道如果我们一开始就没有隐式转换,这不会成为问题。但这是一个大型遗留代码库,我无法轻易更改。)

控制台应用程序测试代码:

Console.WriteLine(".NET 8"); Console.WriteLine("Input Float: 0.3333333F"); float f1 = 0.3333333F; Console.WriteLine("Decimal: " + f1.ToString()); Console.WriteLine("Binary: " + GetFloatBinary(f1)); Console.WriteLine(); Console.WriteLine("Float multiplication first, then conversion"); Console.WriteLine("f2 = f1 * f1"); float f2 = f1 * f1; Console.WriteLine("Decimal: " + f2.ToString()); Console.WriteLine("Binary: " + GetFloatBinary(f2)); Console.WriteLine("d1 = (double)f2"); double d1 = (double)f2; Console.WriteLine("Decimal: " + d1.ToString()); Console.WriteLine("Binary: " + GetDoubleBinary(d1)); Console.WriteLine(); Console.WriteLine("Conversion first, multiplication second"); Console.WriteLine("d2 = (double)f1 * (double)f1"); double d2 = (double)f1 * (double)f1; Console.WriteLine("Decimal: " + d2.ToString()); Console.WriteLine("Binary: " + GetDoubleBinary(d2)); Console.WriteLine(); Console.WriteLine("Let the platform decide"); Console.WriteLine("d3 = f1 * f1"); double d3 = f1 * f1; Console.WriteLine("Decimal: " + d3.ToString()); Console.WriteLine("Binary: " + GetDoubleBinary(d3)); Console.WriteLine(); Console.ReadLine(); static string GetFloatBinary(float value) { const int bitCount = sizeof(float) * 8; int intValue = System.BitConverter.ToInt32(BitConverter.GetBytes(value), 0); return Convert.ToString(intValue, 2).PadLeft(bitCount, '0'); } static string GetDoubleBinary(double value) { const int bitCount = sizeof(double) * 8; int intValue = System.BitConverter.ToInt32(BitConverter.GetBytes(value), 0); return Convert.ToString(intValue, 2).PadLeft(bitCount, '0'); }

结果:

.NET FRAMEWORK Input Float: 0.3333333F Decimal: 0.3333333 Binary: 00111110101010101010101010101010 Float multiplication first, then conversion f2 = f1 * f1 Decimal: 0.1111111 Binary: 00111101111000111000111000110111 d1 = (double)f2 Decimal: 0.111111097037792 Binary: 0000000000000000000000000000000011100000000000000000000000000000 Conversion first, multiplication second d2 = (double)f1 * (double)f1 Decimal: 0.111111097865635 Binary: 0000000000000000000000000000000011100011100011100011100100000000 Let the platform decide d3 = f1 * f1 Decimal: 0.111111097865635 Binary: 0000000000000000000000000000000011100011100011100011100100000000

.NET 8
Input Float: 0.3333333F
Decimal: 0.3333333
Binary: 00111110101010101010101010101010

Float multiplication first, then conversion
f2 = f1 * f1
Decimal: 0.1111111
Binary: 00111101111000111000111000110111
d1 = (double)f2
Decimal: 0.1111110970377922
Binary: 0000000000000000000000000000000011100000000000000000000000000000

Conversion first, multiplication second
d2 = (double)f1 * (double)f1
Decimal: 0.11111109786563489
Binary: 0000000000000000000000000000000011100011100011100011100100000000

Let the platform decide
d3 = f1 * f1
Decimal: 0.1111110970377922
Binary: 0000000000000000000000000000000011100000000000000000000000000000
我想我清楚地了解发生了什么变化,但我找不到任何文档说明原因。

感谢您的见解!

c# .net .net-core implicit-conversion
1个回答
0
投票

两者的 IL 是相同的,并且 ECMA 附录中没有提及,因此可能在这方面没有真正的变化。

在 64 位上,.NET Framework 与我的情况下的 .NET 6 相同(并且结果与所讨论的结果相同) - 首先乘以浮点数。

所以,我认为这是期望的行为。

在 32 位上,.NET Framework 的反汇编是:

00120AD2 D9 45 C0 fld dword ptr [ebp-40h] 00120AD5 D8 C8 fmul st,st(0) 00120AD7 DD 5D A4 fstp qword ptr [ebp-5Ch]

关于
fld

说明

fld 指令加载 32 位、64 位或 80 位浮点 值入栈。该指令
转换 32 位和 64 位 在压入该值之前将操作数转换为 80 位扩展精度值 到浮点堆栈上。

所以这似乎是罪魁祸首,可能是他们后来在 .NET(核心)中修复的错误。

© www.soinside.com 2019 - 2024. All rights reserved.