Floating point 舍入问题.Net Core 3.1与.Net Core 2.0/.Net Framework

Floating point 舍入问题.Net Core 3.1与.Net Core 2.0/.Net Framework,floating-point,rounding,precision,.net-framework-version,.net-core-3.1,Floating Point,Rounding,Precision,.net Framework Version,.net Core 3.1,我在.NET Core 3.0和.NET Framework/.NET Core 2.x之间遇到了一些取整问题 我已经在网上搜索了一段时间,但是我找不到合适的词来搜索,所以我把它贴在这里 我编写了以下示例控制台应用程序来说明我的问题: 类程序 { 静态void Main(字符串[]参数) { 常数双x=123.4567890/3.14159265358979; 控制台写入线(x); 常数双y=98.76543210/3.14159265358979; 控制台写入线(y); 常数双z=11.223

我在.NET Core 3.0.NET Framework/.NET Core 2.x之间遇到了一些取整问题

我已经在网上搜索了一段时间,但是我找不到合适的词来搜索,所以我把它贴在这里

我编写了以下示例控制台应用程序来说明我的问题:

类程序
{
静态void Main(字符串[]参数)
{
常数双x=123.4567890/3.14159265358979;
控制台写入线(x);
常数双y=98.76543210/3.14159265358979;
控制台写入线(y);
常数双z=11.2233445566778899/3.14159265358979;
控制台写入线(z);
Console.ReadKey();
}
}
我在不同的框架上运行了这个程序,得到了以下输出:

  • .NET Framework 4.7.2
    • 392975164552063
    • 314380134506439
    • 357250152843761
  • .NET核心2.0:
    • 392975164552063
    • 314380134506439
    • 357250152843761
  • .NET核心3.0:
    • 392975164552063
    • 31438013450643936
    • 35725015284376096
如您所见,3.0输出与前两个不同,并且从浮点后的第13个数字开始具有更高的精度

我假设.NET Core 3.0的精度更高

但我的情况是,我想从.NET Framework迁移到.NET Core 3.0。在迁移之前,我为.Net Framework库编写了测试,以确保在迁移到.Net Core 3.0后,计算将给出相同的输出。为此,我编写了如下测试:

//排列
常数双期望值=0.1232342802302;
//表演
var result=Subject.Calculate();
//断言
结果应为(期望值);
如果我迁移代码并运行测试(我将其写入.NET Framework),测试将失败。我有一些细微的差别,比如

预期项[0]为0.4451391569556069,但找到0.44513915698437145。
预期结果为-13.142181869094,但发现为-13.142181869062。
我的问题是,;我如何强制使用与.NET Framework/.NET Core 2.0相同的方法对.NET Core 3.0进行取整,因此我不会得到这些细微差别


谁能解释一下这种差异/描述一下.NET Core 3.1.NET Framework的舍入变化?

这太奇怪了。。。。我已经设置了一个包含4个项目的解决方案

  • NET Framework 4.7.2项目
  • NET核心2.0项目
  • NET核心3.1项目
  • NET Core 3.1项目,一次运行所有3个项目
在每个项目中,我都使用
Math.PI
常量来查看是否有什么变化,事实上确实如此,但不是我所期望的

如果我运行第四个项目,调用所有3个的项目,我会得到这个结果

因此,所有3个项目的值都是相同的。 但如果我单独运行这些,我会得到以下结果:

.NET框架

.NET核心2

.NET核心3

因此,出于某种原因,我在.NET内核中使用
Math.PI
常量得到了与您不同的结果,这些结果在版本2和版本3.1之间是相同的。但是,我得到的结果与使用.NET Framework得到的结果相同,这与两个.NET内核不同。但正如我们在上面看到的,如果您从.NETCore中生成的另一个项目运行所有3个项目,您会得到相同的结果,这意味着可能是调用项目决定应该使用什么舍入。不幸的是,我找不到发生这种情况的确切原因,但是如果我没记错的话,在Windows和Unix系统中舍入的工作方式有一些细微的差别。由于.NET Core是跨平台的,我认为它使用的是Unix舍入,而不是.NET Framework可能使用的Windows舍入,这导致了这些差异

编辑:这已经超越了科学的范畴了。。。 我使用了
3.14159265358979
的常量值,而不是
Math.PI
,这在理论上是相同的(根据)。但使用此值后,结果会再次发生变化!如果在所有3个项目都在运行的地方运行测试,您仍然会得到所有3个项目的相同结果,但这些结果与上一次运行的结果不同

39,2975164552063
31,438013450643936
3,5725015284376096
启动.NET Framework项目时,您会得到与以前相同的结果,而运行.NET核心项目时,您会得到上述结果。因此,使用常量值,而不是
Math.PI
,再次更改结果。但这是毫无意义的,因为在引擎盖下,
Math.PI
只是一个双常量,值为
3.14159265358979

编辑2:我用Python编写了相同的程序

def main():
    x = 123.4567890 / 3.14159265358979
    print(x)
    y = 98.76543210 / 3.14159265358979
    print(y)
    z = 11.2233445566778899 / 3.14159265358979
    print(z)


if __name__ == "__main__":
    main()

结果与.NETCore相同

39.2975164552063
31.438013450643936
3.5725015284376096
然后我试着用Go做同样的事情

package main

import "fmt"

func main() {
    x := 123.4567890 / 3.14159265358979
    fmt.Println(x)
    y := 98.76543210 / 3.14159265358979
    fmt.Println(y)
    z := 11.2233445566778899 / 3.14159265358979
    fmt.Println(z)
}
在这种情况下,结果如下

39.2975164552063
31.43801345064394
3.5725015284376096
y
已四舍五入为
.94
,而
x
z
与python和.NET核心相同

作为最后一个测试,我尝试使用Javascript/Node.JS来实现这一点

let x = 123.456789 / 3.14159265358979;
console.log(x);
let y = 98.7654321 / 3.14159265358979;
console.log(y);
let z = 11.2233445566778899 / 3.14159265358979;
console.log(z);

但这里的结果也与python和.NETCore相同

39.2975164552063
31.438013450643936
3.5725015284376096

从Python、JS、.NETCARE和GO(如果你不考虑<代码> y>代码>舍入),都是跨平台的,我假设有一些与.NETFramework依赖的Windows生态系统相关的东西。尝试与Windows相关的其他框架/语言会很有趣,但除了.NET Framework(可能是Visual Basic?)之外,我不知道还有其他框架/语言。

这是一个有文档记录的更改,使格式化程序和解析器符合IEEE 754-2008。从.NET 3.0的新增功能
文档中的部分:

漂浮
const double y = 98.76543210 / 3.14159265358979;
Console.WriteLine(y);
Console.WriteLine("{0:G15}",y);
31.438013450643936
31.4380134506439