源于MATLAB中cast和typecast函数数据格式的探讨
2020-08-13王选张欣李晓欧严加勇白宝丹查雨彤单纯玉
王选 张欣 李晓欧 严加勇 白宝丹 查雨彤 单纯玉
[摘 要] MATLAB是工程计算中应用非常广泛的一种软件,熟练掌握MATLAB是大部分工科类学生所必备的技能。通过对MATLAB教材中习题的解答,分析了cast和typecast两个函数,介绍了大端和小端两种字节序类型,以及整数及浮点数在计算机中的存储格式。由此,可以使学生学会使用MATLAB函数,并了解数据在计算机中存储格式。
[关键词] MATLAB;IEEE 754;字节序;数据格式
[基金项目] 上海市教委重点课程建设项目“数字信号处理”(B1-0200-19-309411);上海健康医学院教学建设项目“基于穿戴式医疗技术与器械工程研究中心的生物医学工程本科创新能力个性化培养‘四个一工程”(B1-0200-19-309302)
[作者简介] 王 选,博士,上海健康医学院医疗器械学院讲师,主要从事生物医学信号的分析与处理研究;单纯玉,博士,上海健康医学院医疗器械学院教授(通信作者),主要从事医学仪器设计与开发研究。
[中图分类号] G642.0 [文献标识码] A [文章编号] 1674-9324(2020)29-0363-03 [收稿日期] 2019-10-10
MATLAB是美国MathWorks公司出品的商业数学软件,用于算法开发、数据可视化、数据分析以及数值计算的高级技术计算语言和交互式环境。MATLAB课程在很多高校中已成为工科类学生必修的专业基础课,熟练掌握MATLAB已成为相关专业学生必备的技能之一。
一、教材中的习题
在教学中,我们的教材采用了张威编著的《MATLAB基礎与编程入门》(第三版),该教材受中国电子教育学会高教分会推荐,是普通高等教育电子信息类”十三五“规划教材。该教材第3章数据类型的章后练习第1题为[1]“在进行数值类型数据转换时,可以使用cast或typecast函数,例如a=-1,若使用这两个函数将变量a转换为无符号的8位整数,计算的结果是什么?”
针对此题,我们利用MATLAB 2015b运行如下的代码
a=-1;
b=cast(a,'uint8')
c=typecast(a,'uint8')
得到结果为
b=0
c=0 0 0 0 0 0 240 191
虽然结果运行出来,但是为何会出现这个结果?这个结果令学生比较困惑,本文将对问题进行解答,同时介绍数据在计算机中的存储格式。
二、cast函数和typecast函数
查询MATLAB的帮助文档,可知cast函数会截取数据以适应新的数据类型,它会改变内存中的数据。已知无符号8位整数‘uint8的数值表示范围为0~2^8-1,即0~255。故a=-1时,则得到运行结果b=0。对如下数值进行转换,有cast(-2.5,'uint8')=0;cast(25.5,'uint8')=26;%四舍五入cast(300,'uint8')=255;%最大为255同理对于有符号16位整数‘int16,其数值表示范围为-2^15~2^15-1,有cast(-20.5,'int16')=-21cast(-65536,'int16')=-32768,即-2^15.
查询MATLAB的帮助文档,可知typecast函数与cast函数不同,它在进行数据类型转换时,不会截取数据,不会改变内存中数据。对其结果的解释需要了解数据在内存中的存储格式。
三、内存中的数据存储格式
(一)大端和小端字节序
字节序是指整数在内存中保存的顺序,有大端(big-endian)和小端(little-endian)两种类型。1980年,Danny Cohen在其著名的论文“On Holy Wars and a Plea for Peace”中为了平息一场关于在消息中字节该以什么样的顺序进行传送的争论而引用了大端和小端两个词。[2]他非常形象贴切地把支持从一个消息序列的最高位开始传送的那伙人叫作Big-Endians,支持从最低位开始传送的相对应的叫作Little-Endians。
在计算机的内存中,小端字节序将低序字节存储在起始地址(低位编址),而大端是将高序字节存储在起始地址(高位编址)。例如对于857的存储,两种字节序的存储方式如表1所示。
小端字节序最符合人的思维,即地址低位存储值的低位,地址高位存储值的高位。而大端字节序是最直观的字节序,即地址低位存储值的高位,地址高位存储值的低位。只需要把内存地址从左到右按照由低到高的顺序写出,把值按照通常的高位到低位的顺序写出;两者对照,一个字节一个字节的填充进去。
计算机中不同的CPU有不同的字节序类型,x86系列的CPU都是小端的字节序。
(二)整数类型
1.正数。数据在内存中是以补码存储的,正数的补码与原码相同。
对于数值1,其补码为“0000 0001”,则typecast函数保持其补码不变,故为typecast(int8(1),'uint8')=1。同理,对于有符号32位整数来说,256的补码为“0000 0000 0000 0000 0000 0001 0000 0000”。
如果将其转换为无符号8位整数,则上述补码以8位为一个单位进行分隔,得到“0000 0000,0000 0000, 0000 0001, 0000 0000”。
以小端字节序看待,低位地址存值的低位,高位地址存高位。故执行命令typecast(int32(256),'uint8')得到结果为“0 1 0 0”。
如果将256转换为无符号16位整数,则补码以16位为一个单位进行分隔,得到“0000 0000 0000 0000, 0000 0001 0000 0000”。
再以小端字节序看待,故执行命令typecast(int32(256),'uint16')得到结果为“256 0”。
3.负数。对于负数,补码与原码不同。
例如于有符号8位整数,-2的原码为“1000 0010”,反码为“1111 1101”,补码为“1111 1110”。
typecast函数保持其补码不变,故将其转换为无符号8位整数时,故为typecast(int8(-2),'uint8')=254。
若-2为有符号16位整数,则原码为“1000 0000 0000 0010”,反码为“1111 1111 1111 1101”,补码为“1111 1111 1111 1110”,将其转换为无符号8位整数时,则8位为一个单位进行分隔,有“1111 1111,1111 1110”。以小端字节序看待,故执行倒序typecast(int16(-2),'uint8')得到结果为254 255。
(三)浮点数
对于浮点数的表示,IEEE(Institute of Electrical and Electronics Engineers,电子电气工程师协会)在1985年制定了二进制浮点数算术标准(ANSI/IEEE Std 754-1985),又称IEC 60559:1989,它是微处理器系统的二进制浮点数算术。[3,4]根据此标准,一个单精度浮点数用32位二进制数存储,最高位第32位为符号位,中间的第24-31位为指数部分,最后第1-23位为小数部分。双精度浮点数用64位二进制数存储,最高位第64位为符号位,中间的第53-63位为指数部分,最后第1-52位为小数部分,如表2所示。
即二进制为“1011 1111 1111 0000 0000 ……”。
将其转换为无符号8位整数,则以8位为一个单位进行分隔,得到“1011 1111,1111 0000,0000 ……”。以小端字节序看待,故执行命令typecast(-1,'uint8')得到结果为“0 0 0 0 0 0 240 191”。即之前的运行结果。
同理若將其转换为无符号16位整数,则以16位为一个单位进行分隔,得到“1011 1111 1111 0000,0000 ……”。以小端字节序看待,故执行命令typecast(-1,'uint16')得到结果为“ 0 0 0 49136”。
四、结果与讨论
通过以上对MATLAB的运行结果的解释,我们介绍了两个函数的特点,以及整数及浮点数在内存的存储格式。因为MATLAB默认的格式为双精度浮点数,故本文对双精度浮点数作了详细介绍,单精度浮点数的情况类似。此外,我们只针对习题分析了双精度浮点数转换为无符号整数的情况,如果要转换为其他类型的数据,同理可以进行分析。
总之,通过以上分析,加深了学生对数据格式的理解,并启发学生面对困惑,要查找资料进行解决。
参考文献
[1]张威.MATLAB基础与编程入门[M].第3版.西安:西安电子科技大学出版社,2017.
[2]Cohen D.On Holy Wars and a Plea for Peace[J].Computer,1981(14):48-54.
[3]朱亚超.基于IEEE754的浮点数存储格式分析研究[J].计算机与信技术,2006(9):50-52.
[4]杨军.基于IEEE的浮点数格式分析及转换方法研究[J].甘肃科技纵横,2009,38(5):19-20.