c# – 包含的表达式树给出了与等效代码不同的结果

c# – 包含的表达式树给出了与等效代码不同的结果,第1张

概述以下代码: double c1 = 182273d;double c2 = 0.888d;Expression c1e = Expression.Constant(c1, typeof(double));Expression c2e = Expression.Constant(c2, typeof(double));Expression<Func<double, double>> sine 以下代码:
double c1 = 182273d;double c2 = 0.888d;Expression c1e = Expression.Constant(c1,typeof(double));Expression c2e = Expression.Constant(c2,typeof(double));Expression<Func<double,double>> sinee = a => Math.Sin(a);Expression sine = ((MethodCallExpression)sinee.Body).Update(null,new[] { c1e });Expression sum = Expression.Add(sine,c2e);Func<double> f = Expression.Lambda<Func<double>>(sum).Compile();double r = f();double rr = Math.Sin(c1) + c2;Console.Writeline(r.ToString("R"));Console.Writeline(rr.ToString("R"));

将输出:

0.0829075149338464880.082907514933846516

为什么r和rr不同?

更新:

发现如果选择“x86”平台目标或使用“任何cpu”检查“首选32位”,则重现此项.在64x模式下正常工作.

解决方法 我不是这方面的专家,但我会对此发表看法.

首先,只有在使用调试标志进行编译时才会出现问题(在发布模式下它不会出现),并且实际上只有在运行为x86时才出现.

如果我们反编译表达式编译的方法,我们将看到这个(在调试和发布中):

IL_0000: ldc.r8       182273 // push first valueIL_0009: call         float64 [mscorlib]System.Math::Sin(float64) // call Math.Sin()IL_000e: ldc.r8       0.888 // push second valueIL_0017: add          // addIL_0018: ret

但是,如果我们查看在调试模式下编译的类似方法的IL代码,我们将看到:

.locals init (  [0] float64 V_0)IL_0001: ldc.r8       182273IL_000a: call         float64 [mscorlib]System.Math::Sin(float64)IL_000f: ldc.r8       0.888IL_0018: add          IL_0019: stloc.0      // save to localIL_001a: br.s         IL_001c // basically nopIL_001c: ldloc.0      // V_0 // pop from local to stackIL_001d: ret          // return

您会看到编译器将(不必要的)结果保存并加载到本地变量(可能用于调试目的).现在我不确定,但据我所知,在x86架构上,双值可能存储在80位cpu寄存器中(引自here):

By default,in code for x86 architectures the compiler uses the
coprocessor’s 80-bit registers to hold the intermediate results of
floating-point calculations. This increases program speed and
decreases program size. However,because the calculation involves
floating-point data types that are represented in memory by less than
80 bits,carrying the extra bits of precision—80 bits minus the number
of bits in a smaller floating-point type—through a lengthy calculation
can produce inconsistent results.

所以我的猜测是这个本地存储和从本地加载导致从64位转换到80位(因为寄存器)和返回,这会导致你观察到的行为.

另一种解释可能是JIT在调试和释放模式之间表现不同(可能仍然与将中间计算结果存储在80位寄存器中有关).

希望有些知道更多的人可以确认我是否正确.

更新以回应评论.反编译表达式的一种方法是创建动态程序集,将表达式编译到那里的方法,保存到磁盘,然后查看任何反编译器(我使用JetBrains dotpeek).例:

var asm = AppDomain.CurrentDomain.defineDynamicAssembly(     new Assemblyname("dynamic_asm"),AssemblyBuilderAccess.Save); var module = asm.defineDynamicModule("dynamic_mod","dynamic_asm.dll"); var type = module.defineType("DynamicType"); var method = type.defineMethod(     "Dynamicmethod",MethodAttributes.Public | MethodAttributes.Static); Expression.Lambda<Func<double>>(sum).CompiletoMethod(method); type.CreateType(); asm.Save("dynamic_asm.dll");
总结

以上是内存溢出为你收集整理的c# – 包含的表达式树给出了与等效代码不同的结果全部内容,希望文章能够帮你解决c# – 包含的表达式树给出了与等效代码不同的结果所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1249848.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-07
下一篇 2022-06-07

发表评论

登录后才能评论

评论列表(0条)

保存