day*_*oli 4 c++ compiler-construction integer-overflow type-conversion floating-point-conversion
据我所知,在C++中,当我转换float/ double成int,由此浮点数超出该范围内int可容纳,则结果没有被定义为C++语言的一部分.结果取决于实现/编译器.常见的编译器使用什么策略来处理这个问题?
转换7.2E12为a int可以产生值1634811904或2147483647.例如,有没有人知道编译器在每种情况下做了什么?
Pas*_*uoq 10
编译器生成指令序列,为不会导致溢出的所有输入生成正确的结果.这是它必须担心的(因为从浮点到整数的转换中的溢出是未定义的行为).编译器不会"处理"溢出,而是完全忽略它们.如果平台上的基础汇编指令引发异常,那很好.如果它们环绕,那很好.如果他们产生荒谬的结果,再次,罚款.
例如,常量表达式可以在编译时转换为整数,其规则与平台上生成的汇编指令的行为不同.我的博文给出了一个例子:
int printf(const char *, ...);
volatile double v = 0;
int main()
{
int i1 = 2147483648.0;
int i2 = 2147483648.0 + v;
printf("%d %d\n", i1, i2);
}
Run Code Online (Sandbox Code Playgroud)
它生成一个程序,为i1和打印两个不同的值i2.这是因为计算中的转换i1是在编译时应用的,而计算中的转换i2是在运行时应用的.
作为另一个例子,在x86-64平台上从double32位转换为32位的特定情况下unsigned int,结果可能很有趣:
x86指令集中没有指令可以将浮点数转换为无符号整数.
在用于Intel的Mac OS X上,编译64位程序时,从单个指令编译从32位到32位无符号int的转换:64位转换指令cvttsd2siq,目标为64位寄存器,仅限64位寄存器底部的32位随后将用作它代表的32位无符号整数:
$ cat t.c
#include <stdio.h>
#include <stdlib.h>
int main(int c, char **v)
{
unsigned int i = 4294967296.0 + strtod(v[1], 0);
printf("%u\n", i);
}
$ gcc -m64 -S -std=c99 -O t.c && cat t.s
…
addsd LCPI1_0(%rip), %xmm0 ; this is the + from the C program
cvttsd2siq %xmm0, %rsi ; one-instruction conversion
…
Run Code Online (Sandbox Code Playgroud)
这就解释了如何,在该平台上,其结果是模2 32可以用于双打足够小(具体地,小到足以在一个符号的64位整数)来获得.
在旧的IA-32指令集中,没有指令将a转换double为64位有符号整数(并且没有指令将a转换double为32位unsigned int).32位转换unsigned int必须通过结合几个确实存在的指令,包括两个指令来完成cvttsd2si,从双转换为32位有符号整数:
$ gcc -m32 -S -std=c99 -O t.c && cat t.s … addsd LCPI1_0-L1$pb(%esi), %xmm0 ; this is the + from the C program movsd LCPI1_1-L1$pb(%esi), %xmm1 ; conversion to unsigned int starts here movapd %xmm0, %xmm2 subsd %xmm1, %xmm2 cvttsd2si %xmm2, %eax xorl $-2147483648, %eax ucomisd %xmm1, %xmm0 cvttsd2si %xmm0, %edx cmovael %eax, %edx …
分别在%eax和中计算两个替代解决方案%edx.替代方案在不同的定义域上都是正确的.如果要转换的数字in %xmm0大于常数2 31 in %xmm1,则选择一个替代,否则,另一个选择.仅使用从double到int的转换的高级算法将是:
if (d < 231) then (unsigned int)(int)d else (231 + (unsigned int)(int)(d - 231))
将C转换从double转换为unsigned int会产生与它依赖的32位转换指令相同的饱和行为:
$ gcc -m32 -std=c99 -O t.c && ./a.out 123456
0
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
203 次 |
| 最近记录: |