问题描述
这是一段示例代码,具有来自.net core 2.2和3.1的输出。它显示了基本浮点表达式a ^ b的不同计算结果。
Here is a sample piece of code with outputs from .net core 2.2 and 3.1. It shows different computational results for a basic floating point expression a^b.
在此示例中,我们计算出1.9的3的幂。以前的.NET框架产生了正确的结果,但是.net core 3.0和3.1产生了不同的结果。
In this example we calculate 1.9 to the power of 3. Previous .NET frameworks yielded the correct result, but .net core 3.0 and 3.1 yields a different result.
这是否是预期的更改,我们如何在保证数值计算仍会产生相同结果的情况下将财务计算代码迁移到新版本? (如果.NET也具有十进制数学库,那就太好了。)
Is this an intended change and how can we migrate financial calculation code to the new version with a guarantee that numerical calculations will still yield the same results? (It would be nice if .NET had a decimal Math library too).
public static class Program
{
public static void Main(string[] args)
{
Console.WriteLine("--- Decimal ---------");
ComputeWithDecimalType();
Console.WriteLine("--- Double ----------");
ComputeWithDoubleType();
Console.ReadLine();
}
private static void ComputeWithDecimalType()
{
decimal a = 1.9M;
decimal b = 3M;
decimal c = a * a * a;
decimal d = (decimal) Math.Pow((double) a, (double) b);
Console.WriteLine($"a * a * a = {c}");
Console.WriteLine($"Math.Pow((double) a, (double) b) = {d}");
}
private static void ComputeWithDoubleType()
{
double a = 1.9;
double b = 3;
double c = a * a * a;
double d = Math.Pow(a, b);
Console.WriteLine($"a * a * a = {c}");
Console.WriteLine($"Math.Pow(a, b) = {d}");
}
}
.NET Core 2.2
---小数---------
--- Decimal ---------
a * a * a = 6.859
Math.Pow((double) a, (double) b) = 6.859
---双重----------
--- Double ----------
a * a * a = 6.859
Math.Pow(a, b) = 6.859
。 NET Core 3.1
---十进制---------
--- Decimal ---------
a * a * a = 6.859
Math.Pow((double) a, (double) b) = 6.859
---双----------
--- Double ----------
a * a * a = 6.858999999999999
Math.Pow(a, b) = 6.858999999999999
推荐答案
.NET Core引入了许多。其中之一是IEEE 754-2008格式合规性。
.NET Core introduced a lot of floating point parsing and formatting improvements in IEEE floating point compliance. One of them is IEEE 754-2008 formatting compliance.
.NET Core 3.0之前, ToString()
内部受限制精确到仅 15个位置,产生无法解析回原始字符串。问题的值相差仅一位。
Before .NET Core 3.0, ToString()
internally limited precision to "just" 15 places, producing string that couldn't be parsed back to the original. The question's values differ by a single bit.
在.NET 4.7和.NET Core 3中,实际字节均保持不变。在这两种情况下,都调用
In both .NET 4.7 and .NET Core 3, the actual bytes remains the same. In both cases, calling
BitConverter.GetBytes(d*d*d)
产品
85, 14, 45, 178, 157, 111, 27, 64
另一方面,是 BitConverter。 GetBytes(6.859)
产生:
86, 14, 45, 178, 157, 111, 27, 64
即使在.NET Core 3中,解析 6.859也会产生第二个字节序列:
Even in .NET Core 3, parsing "6.859" produces the second byte sequence :
BitConverter.GetBytes(double.Parse("6.859"))
这只是一点点差异。旧的行为产生了一个字符串,无法将其解析回原始值
This is a single bit difference. The old behavior produced a string that couldn't be parsed back to the original value
此差异解释了这一变化:
The difference is explained by this change :
这就是为什么我们在处理浮动时始终需要指定精度点号。在这种情况下也有所改进:
That's why we always need to specify a precision when dealing with floating point numbers. There were improvements in this case too :
使用 ToString( G15 )
产生 6.859
,而 ToString( G16)
产生 6.858999999999999
,它有16个小数位。
Using ToString("G15")
produces 6.859
while ToString("G16")
produces 6.858999999999999
, which has 16 fractional digits.
这提醒我们,在处理浮点数时,无论是否比较或格式化
That's a reminder that we always need to specify a precision when working with floating point numbers, whether it's comparing or formatting
这篇关于.net core 3与版本2.2产生不同的浮点结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!