C++ 各种类型取值范围 浮点数的二进制表示
文章目录
- Part.I Introduction
-
- Chap.I 预备知识
- Chap.II 小数在计算机中的表示
- Part.II 各类型取值范围
- Part.III 一些技巧
- Reference
Part.I Introduction
首先要说明的一点,不同编译器中相同类型的字节数可能不一样,导致它们们的取值范围不一样。本文主要针对LeetCode
,所以内容仅供参考。
Chap.I 预备知识
首先要了解一下下面的知识:
- 1 位有两种状态:0 或 1
- 1 字节(byte) = 8 位(bit)
- 27=1282^7=12827=128;28=5122^8=51228=512;216=655362^{16}=65536216=65536;232=4.3×1092^{32}=4.3\\times 10^{9}232=4.3×109;263=9×10182^{63}=9\\times 10^{18}263=9×1018;264=1.8×10192^{64}=1.8\\times 10^{19}264=1.8×1019;
- 负数在计算机中的二进制表示方法
Chap.II 小数在计算机中的表示
下图是float
的表示方法
举个例子,我们来表示8.25
- 整数部分为 8,二进制表示为
1000
; - 小数部分为 0.25,小数部分的二进制表示计算方法和整数部分的计算方法恰恰相反,整数部分转换二进制的时候是不断除以 2 得到的,这里就是不断乘以2:
0.25*2 = 0.5
,整数部分为0
,记下:0;0.5*2 = 1.0
,整数部分为1,记下:1,所以0.25
的二进制表示即为0.01
(1*2^{-2}
) - 于是
8.25
的(伪)二进制表示为1000.01
- 根据十进制的科学计数法,二进制的科学计数法可以进行如下类比:
1000.01=1.00001*2^3
- 基于上述,我们便可以直接写出
8.25
的二进制表示了。因为8.25
是正数,所以符号位为0
;指数为3(3+127=130)
,所以1000 0010
(130的二进制表示);小数位为00001
,因为它有 23 位,后补0,所以0000 1000 0000 0000 0000 000
- 所以
8.25
的二进制表示为0 1000 0010 0000 1000 0000 0000 0000 000
检验(暂且不知道如何检验,下面的好像不行):
#include <bitset> cout << "8.25的2进制: " << bitset<64>(8.25) << endl;
Part.II 各类型取值范围
变量名 | 字节数 | 数据范围 |
---|---|---|
char |
1 | -128 ~ 127 |
short |
2 | -32767 ~ 32768 |
unsigned short |
2 | 0 ~ 65535 |
int |
4 | −2.1×109∼2.1×109-2.1\\times 10^{9} \\sim 2.1\\times 10^{9}−2.1×109∼2.1×109 |
unsigned int |
4 | 0∼4.3×1090 \\sim 4.3\\times 10^{9}0∼4.3×109 |
long |
8 | −9×1018∼9×1018-9\\times 10^{18} \\sim 9\\times 10^{18}−9×1018∼9×1018 |
unsigned long |
8 | 0∼1.8×10190 \\sim 1.8\\times 10^{19}0∼1.8×1019 |
long long |
8 | −9×1018∼9×1018-9\\times 10^{18} \\sim 9\\times 10^{18}−9×1018∼9×1018 |
float |
4 | ±3.4×10−38∼±3.4×1038±3.4\\times 10^{-38}\\sim±3.4\\times 10^{38}±3.4×10−38∼±3.4×1038,1 位符号位,8 位指数位,23 位尾数位;小数点后有效数字 7 位(精度) |
double |
8 | ±1.7×10−308∼±1.7×10308±1.7\\times 10^{-308}\\sim±1.7\\times 10^{308}±1.7×10−308∼±1.7×10308,1 位符号位,11 位指数位,52 位小数位;小数点后有效数字 15 位(精度) |
#include <climits>
和 #include <float.h>
头文件其实已经用常量记录了不同变量的数据范围。下面是该头文件中的所有符号常量,我们可以通过这些常量查看不同变量的数据范围。
------------------ #include <climits> -------------------------
CHAR_MIN char 最小值
SCHAR_MAX signed char 最大值
SCHAR_MIN signed char 最小值
UCHAR_MAX unsigned char 最大值
SHRT_MAX short 最大值
SHRT_MIN short 最小值
USHRT_MAX unsigned short 最大值
INT_MAX int 最大值
INT_MIN int 最小值
UINT_MAX unsigned int 最大值
UINT_MIN unsigned int 最小值
LONG_MAX long 最大值
LONG_MIN long 最小值
ULONG_MAX unsigned long 最大值
LLONG_MAX long long 最大值
LLONG_MIN long long 最小值
------------------ #include <float.h> -------------------------
FLT_MANT_DIG float 类型的尾数
FLT_DIG float 类型的最少有效数字位数
FLT_MIN_10_EXP 带有全部有效数的float类型的负指数的最小值(以10为底)
FLT_MAX_10_EXP float 类型的正指数的最大值(以10为底)
FLT_MIN 保留全部精度的 float 类型正数最小值
FLT_MAX float 类型正数最大值
// 还有关于 double 的,就不详细列举了
Part.III 一些技巧
在用 C++ 刷题的时候,很容易因为两个大数相加或相乘导致数据溢出,所以这时候就需要注意了:
- 隐式类型转换:一个大类型与一个小类型做运算,最后得到一个大类型的数据(除非把它赋值给一个小类型)。比如,一个
long
与一个int
做运算,最后得到一个long
,但是如果将结果赋值给一个int
,那么赋值的时候会『截位』。 - 强制类型转换:当两个小类型进行运算的时候,如果运算结果超出了该类型能表示的范围,需用强制类型转换将其转换为大类型。比如,当两个
int
做乘法运算时,可以用long long ans = (long long)a * b;
。后面的a * b
不要括号,也就是说先将一个转换为大类型,然后根据隐式类型转换,得到的结果也是大类型,所以就不会溢出了。要括号的话还是会有问题。
Reference
- C/C++ 各种变量的数据范围
- C++十进制转二进制(含小数部分)