与 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;
第二行:
- Unity先将a和b转为double,然后求和
- Desktop 首先对它们求和,然后转换为 double
这在 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
这是可以理解的,因为:
- 第一行我们做float-summation,然后conversion-to-double;
- 而在第二个我们做 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都不一致...
我有共享 C# 代码的 Unity 应用程序和桌面应用程序,但我遇到了此代码中某些行的不一致问题。例如:
float a = 2.34567890F, b = 1.23456782F;
double d = a + b;
第二行:
- Unity先将a和b转为double,然后求和
- Desktop 首先对它们求和,然后转换为 double
这在 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
这是可以理解的,因为:
- 第一行我们做float-summation,然后conversion-to-double;
- 而在第二个我们做 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都不一致...