如何将位模式 Z'FEDCBA09' 分配给 32 位整数
How to assign bit-pattern Z'FEDCBA09' to a 32bit integer
如何将 boz-literal-constant Z'FEDCBA09'
或最高有效位等于 1 的任何其他位模式分配给整数?
标准规定:
INT(A[,KIND])
: If A
is a boz-literal-constant, the value of the result is the value whose bit sequence according to the model in 16.3 is the same as that of A as modified by padding or truncation according to 16.3.3. The interpretation of a bit sequence whose most significant bit is 1 is processor dependent.
source: Fortran 2018 Standard
因此以下分配 可能 失败(假设 integer
是默认的 32 位):
program boz
implicit none
integer :: x1 = int(Z'FEDCBA09')
integer :: x2 = int(Z'FFFFFFFF')
integer :: x3
data x3/Z'FFFFFFFF'/
end program
使用 gfortran,这仅在添加 -fno-range-check
时有效,但这会引入额外的不良影响:
-fno-range-check
: Disable range checking on results of simplification of constant expressions during compilation. For example, GNU Fortran will give an error at compile time when simplifying a = 1. / 0.
With this option, no error will be given and a will be assigned the value +Infinity. If an expression evaluates to a value outside of the relevant range of [-HUGE():HUGE()]
, then the expression will be replaced by -Inf
or +Inf
as appropriate. Similarly, DATA i/Z'FFFFFFFF'/
will result in an integer overflow on most systems, but with -fno-range-check
the value will "wrap around" and i
will be initialized to -1
instead.
我尝试了以下方法,效果很好但仍然不是 100%
integer(kind=INT32) :: x1 = transfer(real(Z'FEDCBA09',kind=REAL32),1_INT32)
integer(kind=INT32) :: x1 = transfer(real(Z'FFFFFFFF',kind=REAL32),1_INT32)
后一种情况在 gfortran 中失败,因为它抱怨 Z'FFFFFFFF'
代表 NaN。
使用 IOR(0,Z'FEDCBA09')
也失败,因为它使用 INT
转换 boz-literal
问题:如何使用 boz-literal-constant 可靠地分配位模式?也就是说,独立于所使用的编译器(GNU、SUN、PGI、NAG、...)。
答案:目前最可靠的答案是Jim Rodes in :
x = ior(ishft(int(Z'FEDC'),bit_size(x)/2),int(Z'BA09'))
这适用于任何编译器,不需要任何其他数据类型即可成功。
在 gfortran 10.1 发布时,对 -fno-range-check
的需求已被删除。在 10.1 中,您指定的位模式将被视为 32 位无符号整数,并且强制执行二进制补码环绕语义。
您的第一个代码片段添加了 print
语句
program boz
implicit none
integer :: x1 = int(Z'FEDCBA09')
integer :: x2 = int(Z'FFFFFFFF')
integer :: x3
data x3/Z'FFFFFFFF'/
print *, x1, x2, x3
end program
产量
$ gfortran -o z file.f90
$ ./z
-19088887 -1 -1
并且不需要 -fno-range-check
选项。提议的 transfer
方法也是如此:
program boz
use iso_fortran_env
implicit none
integer(kind=INT32) :: x1 = &
& transfer(real(Z'FEDCBA09',kind=REAL32),1_INT32)
integer(kind=INT32) :: x2 = &
& transfer(real(Z'FFFFFFFF',kind=REAL32),1_INT32)
print '(I0,1X,Z8.8)', x1, x1
print '(I0,1X,Z8.8)', x2, x2
end program
返回:
$ gfortran -o z file.f90
$ ./z
-19088887 FEDCBA09
2143289344 7FC00000
注意: gfortran 将sNaN
转换为qNan
,这是一个错误,但没人关心。
如果您受困于旧版本的 gfortran,那么使用
integer
需要使用中间转换的情况
program boz
use iso_fortran_env
implicit none
integer(kind=INT32) :: x1 = &
& transfer(int(Z'FEDCBA09',kind=INT64),1_INT32)
print '(I0,1X,Z8.8)', x1, x1
end program
gfortran 将不断折叠带有 transfer
的语句。您可以通过查看使用 -fdump-tree-original 选项创建的文件来验证这一点。对于这个答案和上一个答案,命令行都很简单 gfortran -o z file.f90
.
当处理不支持无符号整数的语言时,你需要能够测试and/or设置最大可用整数的高位,你可以将值拆分为2个变量并处理高位和低位分开。
一种方法是将上半部分放入一个变量,将下半部分放入另一个变量,这样:
integer :: x1 = int(Z'FEDCBA09')
变成:
integer :: x1Hi = int(Z'FEDC')
integer :: x1Lo = int(Z'BA09')
正如 OP 在编辑中指出的那样,可以使用移位操作将完整值分配给这样的单个变量。我稍微改变了它,以便它可以在 x
超过 32 位的情况下工作。
x = ior(ishft(int(Z'FEDC'), 16), int(Z'BA09'))
另一种可能的方法是为高位设置一个单独的变量。
我之前在 comp.lang.fortran 问过类似的问题:https://in.memory.of.e.tern.al/comp.lang.fortran/thread/3878931
一个实际可用的,尽管 100% 的概率仍然被一些人质疑(见那里)只是使用反向 BOZ constant/string 和 NOT()
它。
而不是
integer, parameter :: i = Z'A0000000'
使用
integer, parameter :: i = NOT(int(Z'5FFFFFFF'))
link 中的分析涉及到标准和数值模型解释的大量细节和细节。
从那时起我就在我的生产代码中使用这个:https://bitbucket.org/LadaF/elmm/src/master/src/rng_par_zig.f90 line 285 which is a translation of http://vigna.di.unimi.it/xorshift/xorshift128plus.c
如何将 boz-literal-constant Z'FEDCBA09'
或最高有效位等于 1 的任何其他位模式分配给整数?
标准规定:
INT(A[,KIND])
: IfA
is a boz-literal-constant, the value of the result is the value whose bit sequence according to the model in 16.3 is the same as that of A as modified by padding or truncation according to 16.3.3. The interpretation of a bit sequence whose most significant bit is 1 is processor dependent.source: Fortran 2018 Standard
因此以下分配 可能 失败(假设 integer
是默认的 32 位):
program boz
implicit none
integer :: x1 = int(Z'FEDCBA09')
integer :: x2 = int(Z'FFFFFFFF')
integer :: x3
data x3/Z'FFFFFFFF'/
end program
使用 gfortran,这仅在添加 -fno-range-check
时有效,但这会引入额外的不良影响:
-fno-range-check
: Disable range checking on results of simplification of constant expressions during compilation. For example, GNU Fortran will give an error at compile time when simplifyinga = 1. / 0.
With this option, no error will be given and a will be assigned the value +Infinity. If an expression evaluates to a value outside of the relevant range of[-HUGE():HUGE()]
, then the expression will be replaced by-Inf
or+Inf
as appropriate. Similarly,DATA i/Z'FFFFFFFF'/
will result in an integer overflow on most systems, but with-fno-range-check
the value will "wrap around" andi
will be initialized to-1
instead.
我尝试了以下方法,效果很好但仍然不是 100%
integer(kind=INT32) :: x1 = transfer(real(Z'FEDCBA09',kind=REAL32),1_INT32)
integer(kind=INT32) :: x1 = transfer(real(Z'FFFFFFFF',kind=REAL32),1_INT32)
后一种情况在 gfortran 中失败,因为它抱怨 Z'FFFFFFFF'
代表 NaN。
使用 IOR(0,Z'FEDCBA09')
也失败,因为它使用 INT
问题:如何使用 boz-literal-constant 可靠地分配位模式?也就是说,独立于所使用的编译器(GNU、SUN、PGI、NAG、...)。
答案:目前最可靠的答案是Jim Rodes in
x = ior(ishft(int(Z'FEDC'),bit_size(x)/2),int(Z'BA09'))
这适用于任何编译器,不需要任何其他数据类型即可成功。
在 gfortran 10.1 发布时,对 -fno-range-check
的需求已被删除。在 10.1 中,您指定的位模式将被视为 32 位无符号整数,并且强制执行二进制补码环绕语义。
您的第一个代码片段添加了 print
语句
program boz
implicit none
integer :: x1 = int(Z'FEDCBA09')
integer :: x2 = int(Z'FFFFFFFF')
integer :: x3
data x3/Z'FFFFFFFF'/
print *, x1, x2, x3
end program
产量
$ gfortran -o z file.f90
$ ./z
-19088887 -1 -1
并且不需要 -fno-range-check
选项。提议的 transfer
方法也是如此:
program boz
use iso_fortran_env
implicit none
integer(kind=INT32) :: x1 = &
& transfer(real(Z'FEDCBA09',kind=REAL32),1_INT32)
integer(kind=INT32) :: x2 = &
& transfer(real(Z'FFFFFFFF',kind=REAL32),1_INT32)
print '(I0,1X,Z8.8)', x1, x1
print '(I0,1X,Z8.8)', x2, x2
end program
返回:
$ gfortran -o z file.f90
$ ./z
-19088887 FEDCBA09
2143289344 7FC00000
注意: gfortran 将sNaN
转换为qNan
,这是一个错误,但没人关心。
如果您受困于旧版本的 gfortran,那么使用
integer
需要使用中间转换的情况
program boz
use iso_fortran_env
implicit none
integer(kind=INT32) :: x1 = &
& transfer(int(Z'FEDCBA09',kind=INT64),1_INT32)
print '(I0,1X,Z8.8)', x1, x1
end program
gfortran 将不断折叠带有 transfer
的语句。您可以通过查看使用 -fdump-tree-original 选项创建的文件来验证这一点。对于这个答案和上一个答案,命令行都很简单 gfortran -o z file.f90
.
当处理不支持无符号整数的语言时,你需要能够测试and/or设置最大可用整数的高位,你可以将值拆分为2个变量并处理高位和低位分开。
一种方法是将上半部分放入一个变量,将下半部分放入另一个变量,这样:
integer :: x1 = int(Z'FEDCBA09')
变成:
integer :: x1Hi = int(Z'FEDC')
integer :: x1Lo = int(Z'BA09')
正如 OP 在编辑中指出的那样,可以使用移位操作将完整值分配给这样的单个变量。我稍微改变了它,以便它可以在 x
超过 32 位的情况下工作。
x = ior(ishft(int(Z'FEDC'), 16), int(Z'BA09'))
另一种可能的方法是为高位设置一个单独的变量。
我之前在 comp.lang.fortran 问过类似的问题:https://in.memory.of.e.tern.al/comp.lang.fortran/thread/3878931
一个实际可用的,尽管 100% 的概率仍然被一些人质疑(见那里)只是使用反向 BOZ constant/string 和 NOT()
它。
而不是
integer, parameter :: i = Z'A0000000'
使用
integer, parameter :: i = NOT(int(Z'5FFFFFFF'))
link 中的分析涉及到标准和数值模型解释的大量细节和细节。
从那时起我就在我的生产代码中使用这个:https://bitbucket.org/LadaF/elmm/src/master/src/rng_par_zig.f90 line 285 which is a translation of http://vigna.di.unimi.it/xorshift/xorshift128plus.c