“32bit 机器上gettimeofday() 返回值的tv_sec 要先强制转成long long类型”的问题
来源:互联网 发布:意大利设计风格知乎 编辑:程序博客网 时间:2024/06/17 00:01
今天遇到一个和gettimeofday()相关奇怪的问题,问题描述如下。
有如下代码,在32bit CentOS 6.3平台上编译后运行
#include <iostream>#include <sys/time.h>using namespace std;int main(void) { std::cout << __WORDSIZE << std::endl; struct timeval tm; gettimeofday(&tm, NULL); cout << "tm.tv_sec: " << tm.tv_sec << endl; cout << "tm.tv_usec: " << tm.tv_usec << endl; long long timestamp = tm.tv_sec * 1000; cout << "timestamp: " << timestamp << endl; return 0;}运行结果如下。
32tm.tv_sec: 1384764898tm.tv_usec: 303888timestamp: 1785428688这里的32表明是32bit机器。tm.tv_sec 和 tm.tv_usec的值都是正确的,但timestamp值异常,应该是1384764898000,结果像是被截断了,成了1785428688。
修改第13行代码如下。即,先将tm.tv_sec强制类型转换成long long 类型,然后再做计算。
#include <iostream>#include <sys/time.h>using namespace std;int main(void) { std::cout << __WORDSIZE << std::endl; struct timeval tm; gettimeofday(&tm, NULL); cout << "tm.tv_sec: " << tm.tv_sec << endl; cout << "tm.tv_usec: " << tm.tv_usec << endl; long long timestamp = (long long)tm.tv_sec * 1000; cout << "timestamp: " << timestamp << endl; return 0;}这样改过后,结果正确。
32tm.tv_sec: 1384764980tm.tv_usec: 331110timestamp: 1384764980000
但是为什么会这样?timestamp是long long类型的,8字节,足够承载最终计算出来的值,所以像是问题出在tm.tv_sec上,但是tm.tv_sec本身输出的值也是正确的~~~
我猜原因大概是这样的:
1384764980 * 1000的值已经超出32位有符号int所能表示的最大上限(2 ^ 31 - 1 = 2147483647)了。
同时,long long timestamp = tm.tv_sec * 1000很可能被编译器先转成了下面的样子(32位系统下,tm.tv_sec的数据类型 time_t 是4字节int)
int tmp = tm.tv_sec * 1000;
long long timestamp = tmp;
在第一步就已经发生溢出了,第二步赋值给long long型变量还是溢出的值!
下面用一个小例子 t.cpp 来验证上面的猜测。
int main(void) {int intValue = 2100000000;long long llValue = intValue * 10;return 0;}
通过 g++ -save-temps -S t.cpp 命令得到g++编译 t.cpp 过程中产生的汇编文件 t.s
基本上就是,用32位的寄存器做乘10的运算,结果也存在32位eax寄存器中(16-21行,这时已经溢出了),然后在赋值给llValue(22-25行)的时候,扩展成64位寄存器(edx, eax)表示。
=======================================
对 t.cpp 稍作修改如下,即在 intValue 做乘10操作前,强制类型转换成 long long 类型
int main(void) {int intValue = 2100000000;long long llValue = (long long)intValue * 10;return 0;}
然后,得到新的汇编代码如下
.file"x.cpp".text.globl main.typemain, @functionmain:.LFB0:.cfi_startproc.cfi_personality 0x0,__gxx_personality_v0pushl%ebp.cfi_def_cfa_offset 8.cfi_offset 5, -8movl%esp, %ebp.cfi_def_cfa_register 5andl$-8, %esppushl%ebxsubl$20, %espmovl$2100000000, 4(%esp)movl4(%esp), %eaxmovl%eax, %edxsarl$31, %edximull$10, %edx, %ecximull$0, %eax, %ebx.cfi_escape 0x10,0x3,0x7,0x75,0x0,0x9,0xf8,0x1a,0x34,0x1caddl%ebx, %ecxmovl$10, %ebxmull%ebxaddl%edx, %ecxmovl%ecx, %edxmovl%eax, 8(%esp)movl%edx, 12(%esp)movl%eax, 8(%esp)movl%edx, 12(%esp)movl$0, %eaxaddl$20, %esppopl%ebx.cfi_restore 3movl%ebp, %esp.cfi_def_cfa_register 4popl%ebp.cfi_restore 5.cfi_def_cfa_offset 4ret.cfi_endproc.LFE0:.sizemain, .-main.ident"GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-3)".section.note.GNU-stack,"",@progbits
大致的意思就是,通过下面两条指令将数字2100000000保存在eax寄存器中:
movl $2100000000, 4(%esp)
movl 4(%esp), %eax
然后通过无符号乘法运算mull对eax做乘10操作,结果的高32位保存在edx中,低32位保存在eax中。
movl $10, %ebx
mull %ebx ;eax * ebx = edx,eax
2100000000的符号位通过算术右移31位获取到,然后也乘10,再加到结果的高32位edx寄存器中。
即,用两个32位寄存器来表示最终的计算结果,于是没有发生溢出!
前面的猜测基本正确~~~
- “32bit 机器上gettimeofday() 返回值的tv_sec 要先强制转成long long类型”的问题
- int/long/long long/double类型的取值范围
- int ,long , long long类型的范围
- int ,long , long long类型的范围
- int ,long , long long类型的范围
- int ,long , long long类型的范围
- int ,long , long long类型的范围
- INT ,LONG , LONG LONG类型的范围
- int ,long , long long类型的范围
- int ,long , long long类型的范围
- int ,long , long long类型的范围
- int ,long , long long类型的范围
- int ,long , long long类型的范围
- int, long, long long类型的范围
- int ,long , long long类型的范围
- int ,long , long long类型的范围
- int ,long , long long类型的范围
- 返回值类型为unsigned long long的函数永远返回为0的问题解决
- Jena——将本体持久化到MySQL数据库中
- 施怡彤老师的新作《变还是不变?职场跳槽是个心理活儿》刊登于(《优家画报》
- IOS本地,APNS远程推送(具体过程)
- java内存设置 (cmd tomcat )
- 全面介绍Windows内存管理机制及C++内存分配实例(三):虚拟内存
- “32bit 机器上gettimeofday() 返回值的tv_sec 要先强制转成long long类型”的问题
- xcode checkout an existing project 出错 unable to connect to a repository at url解决方法
- SUSE11 SCIM 和Flash 常用软件安装
- “黑马程序员”IO流DataInputStream与DataOutputStream的用法
- bat中设置变量等于某命令返回值
- codeblocks 下调试问题
- tomcat部署项目的三种方式
- C# 日期(datetime)格式化大全
- hive中antlr语法文件的编译