.net core 3与版本2.2产生不同的浮点结果 [英] .net core 3 yields different floating point results from version 2.2

查看:288
本文介绍了.net core 3与版本2.2产生不同的浮点结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一段示例代码,具有来自.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引入了许多浮动poi nt解析并格式化IEEE浮点遵从性中的改进 。其中之一是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 :


ToString(),ToString( G)和ToString( R)现在将返回最短的可往返字符串。这样可以确保用户最终得到默认情况下可以工作的东西。

ToString(), ToString("G"), and ToString("R") will now return the shortest roundtrippable string. This ensures that users end up with something that just works by default.

这就是为什么我们在处理浮动时始终需要指定精度点号。在这种情况下也有所改进:

That's why we always need to specify a precision when dealing with floating point numbers. There were improvements in this case too :


对于采用精度的 G格式说明符(例如G3),精度说明符为现在总是受到尊重。对于精度小于15(含)的双精度数和精度小于6(含)的浮点数,这意味着您将获得与以前相同的字符串。对于更高的精度,您将获得多达这么多有效数字

For the "G" format specifier that takes a precision (e.g. G3), the precision specifier is now always respected. For double with precisions less than 15 (inclusive) and for float with precisions less than 6 (inclusive) this means you get the same string as before. For precisions greater than that, you will get up to that many significant digits

使用 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产生不同的浮点结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆