与 C# 桌面应用程序行为不一致的 Unity 数字转换

Unity numeric conversion that inconsistent with C# Desktop apps behavior

我有共享 C# 代码的 Unity 应用程序和桌面应用程序,但我遇到了此代码中某些行的不一致问题。例如:

float a = 2.34567890F, b = 1.23456782F;
double d = a + b;

第二行:

这在 d 中产生了不同的结果。

问题:

有没有办法阻止 Unity 这样做(或让桌面应用程序以同样的方式运行)?有没有其他方法可以解决这种不一致? (当然,不检查每一行并进行所有显式转换以模仿 Unity 行为)


详细说明

考虑这段代码:

using (System.IO.StreamWriter f = System.IO.File.CreateText(filename))
{
    float a = 2.34567890F;
    float b = 1.23456782F;

    double d1 = a + b; // sum and then convert
    f.WriteLine(BitConverter.ToString(BitConverter.GetBytes(d1)));

    double d2 = (double)a + (double)b; // convert and then sum
    f.WriteLine(BitConverter.ToString(BitConverter.GetBytes(d2)));
}

当我 运行 通过 桌面应用程序 (DotNet Core 3.1,在 Windows 上)时,我得到了不同的打印:

00-00-00-**40**-58-A4-0C-40
00-00-00-**50**-58-A4-0C-40

这是可以理解的,因为:

  1. 第一行我们做float-summation,然后conversion-to-double;
  2. 而在第二个我们做 2 conversions-to-double,然后 - double-summation.

但是,在Unity Application(2019.4.11f1,在同一台机器上)中,我得到了第二次打印 - 两次:

00-00-00-**50**-58-A4-0C-40
00-00-00-**50**-58-A4-0C-40

似乎在这两种情况下Unity都是先将2个浮点数转换为double,然后double-summation.

不用说,代码中有很多隐式转换,还有像 Math.Sqrt(a+b) (当 a,b 是浮点数时) return Unity 和 Desktop 下的不同值等无辜的短语结果混乱的道路彼此不同。

这是 .NET 虚拟机之间的区别。如果您查看 Unity 和 .NET Core 生成的 IL 代码,它们会生成相同的 IL:

    IL_0001: ldc.r4 2.34567881
    IL_0006: stloc.0
    IL_0007: ldc.r4 1.23456776
    IL_000c: stloc.1
    IL_000d: ldloc.0
    IL_000e: ldloc.1
    IL_000f: add
    IL_0010: conv.r8

这表明转换发生在添加之后。那么,为什么 Unity 表现得好像转换发生在添加之前?这是因为 Unity 使用的是 Mono 框架(当时是 Mono 框架的旧补丁版本),它似乎在添加步骤中执行转换。如果您 运行 Unity 使用 .NET 核心生成 .dll,您将得到您期望的答案:

00-00-00-40-58-A4-0C-40
00-00-00-50-58-A4-0C-40

但是,如果您 运行 使用与 Unity 捆绑的 Mono(4.x 和 .NET Standard 2.0),您将得到不同的答案:

00-00-00-50-58-A4-0C-40
00-00-00-50-58-A4-0C-40

为了获得更多乐趣,您可以在 Unity 中从 Mono 切换到 IL2CPP,然后您将获得与 .NET Core 匹配的答案! (注意:您需要实际构建应用才能看到这一点。运行 编辑器内将继续使用 Mono 框架)

00-00-00-40-58-A4-0C-40
00-00-00-50-58-A4-0C-40

所以连Unity都不一致...