为什么C#编译器对double→double强制转换生成conv.r8 IL指令
问题:同类型强制转换的IL生成差异
是否存在特殊原因,导致C#编译器在执行double -> double的强制类型转换时,会生成conv.r8指令?
这看起来完全没有必要:int -> int、char -> char等同类型转换都不会生成对应的转换指令,你可以在I2I()方法生成的IL中看到这一点。
示例C#代码:
class Foo { double D2D(double d) => (double) d; int I2I(int i) => (int) i; }
生成的IL代码如下:
.class private auto ansi '<Module> { } // end of class <Module> .class private auto ansi beforefieldinit Foo extends [System.Private.CoreLib]System.Object { // Methods .method private hidebysig instance float64 D2D ( float64 d ) cil managed { // Method begins at RVA 0x2050 // Code size 3 (0x3) .maxstack 8 IL_0000: ldarg.1 IL_0001: conv.r8 IL_0002: ret } // end of method Foo::D2D .method private hidebysig instance int32 I2I ( int32 i ) cil managed { // Method begins at RVA 0x2054 // Code size 2 (0x2) .maxstack 8 IL_0000: ldarg.1 IL_0001: ret } // end of method Foo::I2I .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2057 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Foo::.ctor } // end of class Foo
原因说明
这个差异是浮点数的底层规范要求导致的:
- 通用语言基础结构(CLI)允许求值栈上的浮点数使用比声明类型更高的精度暂存,最典型的就是x86架构下的x87 FPU默认使用80位扩展精度保存浮点值,哪怕变量声明的是64位
double,在运算栈上也可能以更高精度存在。 conv.r8除了做类型转换之外,还有一个核心作用是对浮点值做归一化:强制将栈上的浮点值转换为标准IEEE 754 64位双精度格式,对齐精度、处理异常浮点表示,符合显式类型转换的语义要求,哪怕输入值原本就是double类型也需要执行这一步。- 整数类型不存在这类扩展精度的栈存储规则,同类型强制转换没有任何语义价值,编译器自然会直接优化掉,不会生成多余的转换指令。
不用担心IL层面的conv.r8会带来运行时开销,主流架构(x64、ARM64)的JIT编译器都会识别到这是无实际作用的同类型转换,生成本机机器码时会直接省略该操作。
内容的提问来源于stack exchange,提问作者Vagaus
相关产品推荐
相关产品推荐

