Как назначить битовую комбинацию Z'FEDCBA09 '32-битному целому числу - PullRequest
2 голосов
/ 09 октября 2019

Как мне присвоить целое число boz-literal-constant Z'FEDCBA09' или любой другой битовый шаблон с старшим значащим битом, равным 1?

Стандартные состояния:

INT(A[,KIND]): Если A является буквенной константой boz, значением результата является значение, чья битовая последовательность в соответствии с моделью в 16.3 такая же, как у Aизмененный дополнением или усечением в соответствии с 16.3.3. Интерпретация битовой последовательности, старший бит которой равен 1., зависит от процессора.

источник: Стандарт Fortran 2018

Таким образом, следующие назначения могут потерпеть неудачу (предположим, 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: отключить проверку диапазона для результатов упрощения константных выражений во время компиляции. Например, GNU Fortran выдаст ошибку во время компиляции при упрощении a = 1. / 0.. При использовании этой опции ошибка не будет отображаться, и ей будет присвоено значение + Бесконечность. Если выражение оценивается как значение вне соответствующего диапазона [-HUGE():HUGE()], тогда выражение будет заменено на -Inf или +Inf в зависимости от ситуации. Аналогичным образом, DATA i/Z'FFFFFFFF'/ приведет к целочисленному переполнению в большинстве систем, но с -fno-range-check значение будет "развернуто", и i будет инициализировано вместо -1.

источник: Сборник компиляторов GNU, руководство gfortran

Я попытался выполнить следующее, что работает нормально, но все же не на 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? То есть независимо от используемого компилятора (GNU, SUN, PGI, NAG, ...).

Ответ: Самый надежный ответ в настоящее время дает ДжимRodes in этот комментарий :

x = ior(ishft(int(Z'FEDC'),bit_size(x)/2),int(Z'BA09'))

Это будет работать на любом компиляторе и не требует другого типа данных для успешной работы.

Ответы [ 4 ]

5 голосов
/ 09 октября 2019

Потребность в -fno-range-check была удалена в том, что будет gfortran 10.1, когда оно будет выпущено. В 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, чтоэто ошибка, но никто не заботится.

2 голосов
/ 09 октября 2019

Если вы застряли с более старой версией 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.

1 голос
/ 10 октября 2019

Я задавал подобный вопрос раньше на comp.lang.fortran: https://in.memory.of.e.tern.al/comp.lang.fortran/thread/3878931

Практически пригодный для использования, хотя некоторые все еще сомневались в том, что 100% -ная вероятность была (см. Там) просто использоватьобратная константа / строка BOZ и NOT() it.

Вместо

integer, parameter :: i = Z'A0000000'

используйте

integer, parameter :: i = NOT(int(Z'5FFFFFFF'))

Анализ в ссылке идет к большой детали ик тонким точкам стандартной и числовой интерпретации модели.

С тех пор я использую это в своем производственном коде: https://bitbucket.org/LadaF/elmm/src/master/src/rng_par_zig.f90 строка 285, что является переводом http://vigna.di.unimi.it/xorshift/xorshift128plus.c

1 голос
/ 10 октября 2019

При работе с языками, которые не поддерживают целые числа без знака, и вам необходимо иметь возможность проверить и / или установить старший бит наибольшего доступного целого числа, вы можете разделить значение на 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'))

Другой возможный метод состоит в том, чтобы иметь отдельную переменную только для старшего бита.

...