计算机系统对数值类型的编码、运算、转换原理介绍4

数值类型转换数值类型间的转换,可以分成 2 类: 宽转换 (widening conversion)和 窄转换 (narrowing conversion)。
宽转换指往表示范围更广的类型转换,比如从 int 到 long、从 long 到 float;窄转换则相反。
整型间转换(1)宽转换
整型间的宽转换不会产生溢出,无符号整数场景,高位补零;有符号整数场景,高位补符号位。
// c++int main() { int8_t i1 = 100; cout << int8_t i1: << bitset(i1) << endl; cout << int16_t i1: << bitset((int16_t) i1) << endl; int8_t i2 = -100; cout << int8_t i2: << bitset(i2) << endl; cout << int16_t i2: << bitset((int16_t) i2) << endl; uint8_t i3 = 200; cout << uint8_t i3: << bitset(i3) << endl; cout << uint16_t i3: << bitset((uint16_t) i3) << endl; return 0;}// 输出结果int8_t i1: 01100100int16_t i1: 0000000001100100int8_t i2: 10011100int16_t i2: 1111111110011100uint8_t i3: 11001000uint16_t i3: 0000000011001000(2)窄转换
整型间的窄转换直接进行高位截断,只保留低 n 位。比如 16 位的 int16 转换为 8 位的 int8,直接保留 int16 类型值的低 8 位作为转换结果。
// c++int main() { int16_t i1 = 200; cout << int16_t i1: << bitset(i1) << endl; cout << int8_t i1: << bitset((int8_t) i1) << endl; int16_t i2 = -200; cout << int16_t i2: << bitset(i2) << endl; cout << int8_t i2: << bitset((int8_t) i2) << endl; uint16_t i3 = 300; cout << uint16_t i3: << bitset(i3) << endl; cout << uint8_t i3: << bitset((uint8_t) i3) << endl; return 0;}// 输出结果int16_t i1: 0000000011001000int8_t i1: 11001000int16_t i2: 1111111100111000int8_t i2: 00111000uint16_t i3: 0000000100101100uint8_t i3: 00101100(3)无符号整数与有符号整数间的转换
无符号整数与有符号整数间的转换规则是:
如果两者二进制位数一致,比如 int8 到 uint8 的转换,则二进制数值不变,只是改变编码方式;如果位数不一致,比如 int16 到 uint8 的转换,则二进制数值,先按照宽转换或窄转换规则转换,再改变编码方式。// c++int main() { uint8_t i1 = 200; cout << uint8_t i1, decimal: << +i1 << , binary: << bitset(i1) << endl; cout << int8_t i1, decimal: << +(int8_t) i1 << , binary: << bitset((int8_t) i1) << endl; int16_t i2 = -300; cout << int16_t i2, decimal: << +i2 << , binary: << bitset(i2) << endl; cout << uint8_t i2, decimal: << +(uint8_t) i2 << , binary: << bitset((uint8_t) i2) << endl; return 0;}// 输出结果uint8_t i1, decimal: 200, binary: 11001000int8_t i1, decimal: -56, binary: 11001000int16_t i2, decimal: -300, binary: 1111111011010100uint8_t i2, decimal: 212, binary: 11010100整数与浮点数间转型(1)宽转换
整型到浮点数类型的转换这一方向,为宽转换:
如果浮点数的精度,能够表示整数,则正常转换。如果浮点数精度,无法表示整数,则需要近似,会导致精度丢失。// javapublic static void main(string[] args) { int i1 = 1234567; system.out.printf(int i1: %d, float i1: , i1); system.out.println((float) i1); int i2 = 123456789; system.out.printf(int i2: %d, float i2: , i2); system.out.println((float) i2);}// 输出结果int i1: 1234567, float i1: 1234567.0int i2: 123456789, float i2: 1.23456792e8上述例子中,i2=123456789 超过 float 类型能够表示的精度,所以为近似后的结果 1.23456792e8。
那么,为什么 123456789 会近似为 1.23456792e8?
要解释该问题,首先要把它们转换成二进制表示:
public static void main(string[] args) { ... system.out.println(int i2: + int2binarystr(i2)); system.out.println(float i2: + float2binarystr((float) i2));}// 输出结果int i2: 00000111010110111100110100010101float i2: 01001100111010110111100110100011接下来,我们根据 ieee 浮点数的编码规则,尝试将 int i2 转换成 float i2:
int i2 的二进制 ,可以写成 ,对应到 的形式,可以确认 s = 0,e = 26,m = 1.11010110111100110100010101。float 类型中 k = 8,有,,得出 e = 153,按 k 位无符号编码表示为 。同理,由 ,但由于 float 类型的 n = 23,而 m 一共有 26 位,因此需要按照 round-to-even 规则,对 0.11010110111100110100010101进行近似,保留 23 位小数,得到 0.11010110111100110100011,所以 m 为最后,将 s、e、m 按照 float 单精度的编码格式组合起来,就是 ,转换成十进制,就是 1.23456792e8。(2)窄转换
浮点数类型到整型的转换这一方向,为窄转换:
如果浮点数的整数部分,能够用整型表示,则直接舍去小数,保留整数部分。如果超出了整型范围,则结果为该整型的最大/最小值。// javapublic static void main(string[] args) { float f1 = 12345.123f; system.out.print(float f1: ); system.out.print(f1); system.out.printf(, int f1: %d\\n, (int) f1); float f2 = 1.2345e20f; system.out.print(float f2: ); system.out.print(f2); system.out.printf(, int f2: %d\\n, (int) f2); float f3 = -1.2345e20f; system.out.print(float f3: ); system.out.print(f3); system.out.printf(, int f3: %d\\n, (int) f3); }// 输出结果float f1: 12345.123, int f1: 12345float f2: 1.2345e20, int f2: 2147483647float f3: -1.2345e20, int f3: -2147483648浮点数间转型(1)宽转换
单精度 float 到 双精度 double 为宽转换,不会出现精度丢失的问题。
对于 ,规则如下:
s 保持不变。在 e 保持不变的前提下,因为 float 的 k = 8,而 double 的 k = 11,所以两者的 e 会有所不同。在 m 保持不变的前提下,float 的 n = 23,而 double 的 n =52,所以 m 需要低位补 52 - 23 = 29 个 0。// javapublic static void main(string[] args) { float f1 = 1.2345e20f; system.out.print(float f1: ); system.out.print(f1); system.out.print(, double f1: ); system.out.println((double) f1); system.out.println(float f1: + float2binarystr(f1)); system.out.println(double f1: + double2binarystr((double) f1));}// 输出结果float f1: 1.2345e20, double f1: 1.2344999897320129e20float f1: 01100000110101100010011011010000double f1: 0100010000011010110001001101101000000000000000000000000000000000
(2)窄转换
double 到 float 为窄转换,会存在精度丢失问题。
如果 double 值超出了 float 的表示范围,则转换结果为 infinity:
// javapublic static void main(string[] args) { double d1 = 1e200; system.out.print(double d1: ); system.out.println(d1); system.out.print(float d1: ); system.out.println((float) d1); double d2 = -1e200; system.out.print(double d2: ); system.out.println(d2); system.out.print(float d2: ); system.out.println((float) d2); }// 输出结果double d1: 1.0e200float d1: infinitydouble d2: -1.0e200float d2: -infinity如果 double 值还在 float 的表示范围内,则按照如下转换规则:
s 保持不变。在 e 保持不变的前提下,因为 float 的 k = 8,而 double 的 k = 11,所以两者的 e 会有所不同。对于 m,因为 float 的 n = 23,而 double 的 n = 52,所以转换到 float 之后,需要进行截断,只保留高 23 位。// javapublic static void main(string[] args) { double d1 = 3.267393471324506; system.out.print(double d1: ); system.out.println(d1); system.out.print(float d1: ); system.out.println((float) d1); system.out.println(double d1: + double2binarystr(d1)); system.out.println(float d1: + float2binarystr((float) d1));}// 输出结果double d1: 3.267393471324506float d1: 3.2673936double d1: 0100000000001010001000111001111100110000001101000000010101110110float d1: 01000000010100010001110011111010
最后本文花了很长的篇幅,深入介绍了计算机系统对数值类型的编码、运算、转换的底层原理。
数值类型间的转换是最容易出现隐藏 bug 的地方 ,特别是无符号整数与有符号整数之间的转换。所以,很多现代的编程语言,如 java、go 等都不再支持无符号整数,根除了该隐患。
另外,浮点数的编码方式,注定它只能精确表示一小部分的数值范围,大部分都是近似,所以才有了不能用等号来比较两个浮点数的说法。
数值类型虽然很基础,但使用时一定要多加小心。希望本文能够加深你对数值类型的理解,让你写出更健壮的程序。

5G时代,万物互联成为产业发展趋势
疫情已被证明是云应用和扩展的加速器,将继续推动云计算发展
红米将全产品线布局,雷军劝说两年卢伟冰终加入
照明光源的频闪是怎么回事
LG将展示最新一代可卷式超高清OLED显示屏
计算机系统对数值类型的编码、运算、转换原理介绍4
掌握可穿戴设备超长续航的秘密
LED产业如何高质量发展
为什么物联网离不开边缘计算
ECU用VR技术模拟培训大规模伤亡事件
福建铁路和福建铁塔成功实现了南龙铁路网络的全面覆盖
学抖音、快手不成,百度、阿里、腾讯的这些短视频产品都失败了
IT66311 HDMI2.0重新定时缓冲器概述
手机电池剩一格时不要使用
变频调速器的工作原理
e络盟为亚太区扩展来自Bourns、Epcos及威世的无源元件产品系列
腾讯云推出“工业超级大脑”,把腾讯的云计算、AI、大数据等各项技术进一步延伸
Arduino篇—电机的控制
5G芯片市场开启追逐战 多家厂商积极布局
二位三通电磁阀的工作原理