[root@centos7 ~]# gcc main.c -o main main.c: In function ‘main’: main.c:9:27: warning: integer overflow in expression [-Woverflow] u_int64_t total = 2000*10000000; ^ [root@centos7 ~]# cat main.c #include <unistd.h> #include <stdio.h> #include <sys/types.h> int main(int argc,char* argv[]) { //u_int64_t total = 0xFFFFFFFFFFFFFFFF; u_int64_t total = 2000*10000000; //__uint128_t = 2000*10000000; printf("%d, %lu \n",sizeof(u_int64_t), total); printf("%d \n",htons(56710)); return 0; }
每个值都是integer, 但是相乘的值超过了integer最大值(2147483647), 所以编译出错。
解决方案
//将相乘的第一个数转成long即可。
[root@centos7 ~]# gcc main.c -o main [root@centos7 ~]# ./main 8, 20000000000 34525 [root@centos7 ~]# cat main.c #include <unistd.h> #include <stdio.h> #include <sys/types.h> int main(int argc,char* argv[]) { //u_int64_t total = 0xFFFFFFFFFFFFFFFF; u_int64_t total = 2000*10000000L; //__uint128_t = 2000*10000000; printf("%d, %lu \n",sizeof(u_int64_t), total); printf("%d \n",htons(56710)); return 0; }
[root@centos7 ~]# gcc main.c -o main [root@centos7 ~]# ./main 8, 2000000000 8 [root@centos7 ~]# cat main.c #include <unistd.h> #include <stdio.h> #include <sys/types.h> int main(int argc,char* argv[]) { u_int64_t a =200; u_int64_t b =10000000; u_int64_t total = a*b; printf("%d, %lu \n",sizeof(u_int64_t), total); printf("%d \n",sizeof(double)); return 0; }
[root@centos7 ~]# gcc main.c -o main [root@centos7 ~]# ./main 8, 2000000000 8 16 [root@centos7 ~]# cat main.c #include <unistd.h> #include <stdio.h> #include <sys/types.h> int main(int argc,char* argv[]) { u_int64_t a =200; u_int64_t b =10000000; u_int64_t total = a*b; printf("%d, %lu \n",sizeof(u_int64_t), total); printf("%d %d\n",sizeof(double), sizeof(long double)); return 0; }
在存储时,系统将实型数据分成小数部分和指数部分两个部分、分别存储。
如 3.14159 在内存中的存放形式如下:
0.314159 101
算6个有效位
这个决定了精度:
float:有效位6-7位
double:15-16
long double:18-19
当要表示一个精确的数字时候非常重要
否则可能丢失精度。
例子:
float a = 123456789;
float b = 1234567890;
float c = 12345678900;
printf("\n%f\n",a);
printf("\n%f\n",b);
printf("\n%f\n",c);
输出:
123456792.000000
1234567936.000000
12345678848.000000
只有前面7位是精确的后面的数据就未必了。
-----------------------------------
C语言浮点类型有效位(float, double,long double)
#include <stdio.h> int main(void) { float aboat = 32000.0; double abet = 5.32e-5; long double dip = 5.32e-5; printf("%f can be written %e\n", aboat, aboat); printf("%f can be written %e\n", abet, abet); printf("%lf can be written %le\n", dip, dip); return 0; } Output: 32000.000000 can be written 3.200000e+004 0.000053 can be written 5.320000e-005 -1950228512509697500000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000.000000 can be written 2.725000e+002 Press [Enter] to close the terminal ...
果断的出错了…
那么,double内存中占8Byte,64bit,__int64也占8Byte,64bit,按理讲__int64标识double的整数部分应该绰绰有余啊,怎么就出错了呢…
那么我们看下
DBL_MAX:1.7976931348623158e+308
INT64_MAX:9223372036854775807
差这么多,不科学啊,找资料:
https://en.wikipedia.org/wiki/Floating-point_arithmetic
http://blog.csdn.net/tercel_zhang/article/details/52537726
https://wenku.baidu.com/view/437c821152d380eb62946d1f.html
http://www.cnblogs.com/fandong90/p/5397260.html
http://www.fmddlmyy.cn/text60.html
http://blog.csdn.net/gjw198276/article/details/6956244
找了很多,这几篇讲的比较透彻,对解决这一问题有帮助
别急,慢慢读,原来double的解释方式和平常见到的整形表示方式并不一样,它在内存中采用了二进制的科学计数法。
double包含64个bit
第0位符号位,
第1位至12位标识指数位
第13至64位表示尾数
正因为这样,所以他可以表示远超于±2^63之外的数;但这也带来了精度的问题,它并不能完整的标识范围内的每个具体数值,会有误差的存在;它表示的是一个可接受的范围。
[root@centos7 ~]# gcc main.c -o main [root@centos7 ~]# ./main 8, 2000000000 , 0.000000000 8 16 [root@centos7 ~]# cat main.c #include <unistd.h> #include <stdio.h> #include <sys/types.h> int main(int argc,char* argv[]) { u_int64_t a =200; u_int64_t b =10000000; u_int64_t total = a*b; long double db = (long double)total; printf("%d, %lu , %.9lf \n",sizeof(u_int64_t), total, db); printf("%d %d\n",sizeof(double), sizeof(long double)); return 0; }
__div64_32函数 uint32_t __attribute__((weak)) __div64_32(uint64_t *n, uint32_t base) { uint64_t rem = *n; uint64_t b = base; uint64_t res, d = 1; uint32_t high = rem >> 32; /* Reduce the thing a bit first */ res = 0; if (high >= base) { high /= base; res = (uint64_t) high << 32; rem -= (uint64_t) (high*base) << 32; } while ((int64_t)b > 0 && b < rem) { b = b+b; d = d+d; } do { if (rem >= b) { rem -= b; res += d; } b >>= 1; d >>= 1; } while (d); *n = res; return rem; } 宏- -do_div //进制之间的相应转换 # define do_div(n,base) ({ \ unsigned int __base = (base); \ unsigned int __rem; \ //这一句的作用是为了消去警告,因为定义了n变量而没有使用到它,会报警 (void)(((typeof((n)) *)0) == ((unsigned long long *)0)); \ if (((n) >> 32) == 0) { \//32位4字节 __rem = (unsigned int)(n) % __base; \//对应的base进制位 (n) = (unsigned int)(n) / __base; \ } else \ __rem = __div64_32(&(n), __base); \//64转32 __rem; \ })