如何将位模式 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.

source: GNU Compiler Collection, gfortran manual

我尝试了以下方法,效果很好但仍然不是 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