Fortran处理无符号整型unsigned integer

背景:

计算机是以一串二进制数,用约定的表示方式来存储数据的。约定表示方式的不同,造成了可以表示数的范围不同。其中,对于整数类型数据的表示,有unsigned integer(无符号整型)和signed integer(有符号整型)两种方式。其中,无符号整型是所有二进制数都用来表示数值,仅能表示非负整数。故,对于一串长度为N的无符号二进制数,可以表示 0~2N-1之间的整数;

而有符号整型的是第一位二进制数表示数值的正负,后面剩余的位数表示数值。对于一串长度为N的有符号二进制数,可以表示 -2N-1~2N-1-1之间的整数。

比如,对于 1字节(8位)的二进制数,用无符号整型可以表示0~255范围(0~28-1)的整数,用有符号整型可以表示-128~+127范围(-27~27-1)的整数。对于一个8位二进制数,不同的表示方式可能解析出不同的数值:

数值(二进制)

无符号整型

unsigned integer

所有位都用来表示大小

有符号整型

signed intger(kind=1)

第一位表示符号正负。

如果为负数,其数值为补码(按位取反加一)的值

11111111

会被识别成255

会被识别成 -1

第一位为1,表示值为负,其数值为补码:(00000001)

01000000 64

+64

第一位为0,表示值为正,其数值为原码:(01000000)

10000000 128

 

会被识别成 -128

第一位为1,表示值为负,其数值为补码:(10000000)

10000001

 

129

会被识别成 -127

第一位为1,表示值为负,其数值为补码:(01111111)

 

 

高位补零,扩展精度,实现读取无符号整型数据:

由于Fortran不支持无符号整型(unsigned integer)。对这种以无符号整型方式存储的数据,如果仍然使用有符号整型的方式读取,可能会出现结果的错误。

比如,对于一个以2字节(16位)无符号整型方式存储的整数32769,其二进制表示为(1000 0000 0000 0001),超过了2字节有符号整型的表示范围 -32768~32767 ( -216-1~216-1-1)。这时候,如果仍然以有符号短整型变量的格式读取上述数据,则会错误地其识别成-32767(第一位为1,则计算机认为这个数为负数。对这个数的剩余位取补码,得到值32767(即01111 1111 1111 1111),所以这个数被识别成了-32767)。

一个解决办法是,更高长度的有符号整型数表示对于读取的数据,可以将其高位补0(使用zext函数(gfortran编译器不支持)),扩展成更高精度的有符号整数。

在本例子中,如下:

program test
implicit none
!// b'1000000000000001' !//待写入的二进制数。长度16位,十进制值为32769
integer(kind=2) :: xint1 !// 短整型
integer(kind=4) :: xint4 !// 整型

!//待写入文件的二进制数。十进制值为32769
write(*,*)"xint0 :", b'1000000000000001'

!//写二进制数到文件
!//access="stream"必须添加,否则就会以Fortrans顺序存储的方式存储文件。参见https://www.cnblogs.com/jiangleads/p/9022051.html
!//status="replace"必须添加,否则可能出现新保存的文件大小和理论大小不一致。
!//经过试验,Fortran最小的输出单元是4个字节,如果要输出的数据/变量不到4个字节,则剩余位数都会补零。
open(11,file="test.bin",access="stream",form="unformatted",action="write",status="replace")
write(11)b'1000000000000001'
close(11)

!//从文件读二进制数。Fortran只能以有符号的形式读取到整数
open(11,file="test.bin",access="stream",form="unformatted",action="read")
read(11)xint1
close(11)

!//以有符号形式读取到的数。将其识别成了(-32767)
write(*,*)"xint(kind=2) before zext:", xint1

!//对数字的高位补零,扩展到16位(4字节)整型
xint4 = zext(xint1,4)

!//输出到正确的结果(32769)
write(*,*)"xint(kind=4) after zext:",xint4

end program

输出为


xint0 : 32769
xint(kind=2) before zext: -32767
xint(kind=4) after zext: 32769
Press any key to continue . . .

 另注:经过试验,Fortran最小的输出单元是4个字节,如果要输出的数据/变量不到4个字节,则剩余位数都会补零。

利用截断+逻辑运算,转换无符号整型数据(无需扩展精度):

以下为 利用截断+逻辑运算的方法 处理无符号整数的方法。 优点是 不改变数据存储空间的大小, 缺点是不能对转换后的变量进行运算

参考网址 UNSIGNED INTEGERS - Intel Communities (https://community.intel.com/t5/Intel-Fortran-Compiler/UNSIGNED-INTEGERS/m-p/876899)

一些数据是以无符号整数处理的,而标准的Fortran不支持这种类型的数据定义。尽管一些厂商的Fortran (Sun g95)支持定义unsigned定义无符号整型数,但这个方法并不受到Fortran社区推荐。

在许多情况下,可以使用INTEGER(C_INT)进行替代,其中C_INT是内部模块ISO_C_BINDING(10.0及更高版本)中定义的常量,但不要尝试直接使用它进行无符号运算。通常可以得到所需的结果,但有时需要将无符号数通过ZXET函数(0扩展函数,将输入二进制数的左边(高位)补0)转换为INTEGER(8),然后将其截断为INTEGR(C_INT)。 (注:截断方法,使用IBITS函数)

比如

integer(kind=4) :: ui4bytes!//4字节无符号整型数
integer(kind=8) :: i8bytes !//8字节有符号整型数
integer(kind=4) :: i4bytes !//4字节有符号整型数

!//利用zext函数,将4字节无符号整型数,高位补零,扩展到8字节,结果输出到i8bytes
i8bytes = zext(ui4bytes,8)

!//利用ibits函数,将8字节有符号整数,截断选取前4个字节(即从第0位开始,截取32位),结果存储到i4bytes
i4bytes = ibits(i8bytes,0,32)

!//注意:由于截断,原先超出范围的数据无法正常显示

 

截断+逻辑运算进行转换的例子:处理使用unsigned integer 整型 (1个字节):

假设Fortran数组使用 INTEGER, INTEGER(2), INTEGER(4), 和 INTEGER(8),假设数据的值范围是0:255

 一个较为直接的办法是,使用INT函数,将较大的数转换到Integer(1)中:

INTEGER :: in(n)
INTEGER(1) :: out(n)

do i=1,n
out(i) = INT(in(i),1) !//即,将其转换成1字节大小的整数
end do

转换回来的方法,进行逻辑与运算IAND

INTEGER(1) :: in(n)
INTEGER :: out(n)

do i=1,n
out(i) = IAND(INT(in(i)),255)
end do

 

注: 函数IBITS用法

IBITS函数的作用为:截取指定位范围内的位(二进制)序列。用法为RESULT = IBITS(I, POS, LEN)

IBITS从I中提取长度为LEN的字段,从右边往左数(对于字节序为小端Little Endian而言)的第POS位开始(最右边是第0位),向左延伸LEN位。结果右对齐,其余位归零。POS+LEN的值必须小于或等于值BIT_SIZE(I)。

 

例子1: IBITS(12,1,4) 结果是6

上式意思为,对12这个数,取其二进制的第1位,截取长度为4的数据,返回结果。

说明:对于占4个字节的整型数据,12这个数的二进制为 0000 0000 0000 1100,二进制的第1位,也就是从右往左数的第2个位置(因为最右边是第0位),值是0,截取长度为4的数据,就是‘0110’,然后右对齐,剩余位补充0,结果就是二进制的0000 0000 0000 0110,换算成10进制就是6

 

例子2: IBITS(10,1,7)的结果是5

10,其二进制为0000 0000 0000 1010,从右边第1位开始值第7位的数是’0000101‘,右对齐,空位补0之后,就是二进制的0000 0000 0000 0101,换算成10进制就是5。

 

后记:

另外,据Fortran语言论坛介绍1,2,Fortran2008标准中推出了BITS这个类型,但是目前的编译器似乎不支持这个语法规范,若想实现BITS类型,需要自行编译这个库https://github.com/fortran-lang/stdlib

1. https://fortran-lang.discourse.group/t/module-for-dealing-with-unsigned-integers-in-standard-fortran/1242

2. https://fortran-lang.discourse.group/t/unsigned-integer-data-type-in-modern-fortran/1285

3. https://stevelionel.com/drfortran/2020/08/11/doctor-fortran-in-were-all-bozos-on-this-bus

posted @ 2023-02-11 20:50  chinagod  阅读(383)  评论(0编辑  收藏  举报