Фортран 90 - ошибка сегментации

Я делаю программу, которая преобразует десятичное целое число в его двоичное представление. Вот мой код:

program test
implicit none
integer, dimension(:), allocatable :: binary
integer :: decimalnum, i, initvalue

print*, "Enter decimal number to convert: "
read*,initvalue

decimalnum = initvalue
i = 0

do while (decimalnum > 0)
    if (MOD(decimalnum,2)==0) then
        binary(i) = 0                  ! this is as far as the program executes up to 
        decimalnum = decimalnum / 2
        i = i + 1
    else if (MOD(decimalnum,2)==1) then
        binary(i) = 1
        decimalnum = (decimalnum -1) / 2
        i = i + 1
    end if
end do
end program test

В отмеченной точке возвращает ошибку Segmentation fault и завершается с кодом 139.

Почему это происходит?

Заранее спасибо.


person Progrmr    schedule 08.10.2012    source источник
comment
У вас есть доступ к debug? такой gdb   -  person Jack    schedule 08.10.2012
comment
Нет, я не знаю, как им пользоваться.   -  person Progrmr    schedule 08.10.2012
comment
Я действительно мало знаю о Fortran, но я только что посмотрел «распределяемый», и похоже, что вам все еще нужен оператор «распределить», чтобы фактически зарезервировать место.   -  person Nathan Andrew Mullenax    schedule 08.10.2012
comment
Я изменил эту строку на allocate(binary(i) = 0), но компилятор вернул syntax error in allocate statement   -  person Progrmr    schedule 08.10.2012
comment
Похоже, вам нужно выделить все это сразу - нет простого способа выделить дополнительную память по мере необходимости.   -  person Nathan Andrew Mullenax    schedule 08.10.2012
comment
Также: массивы начинаются с индекса 1.   -  person Nathan Andrew Mullenax    schedule 08.10.2012
comment
Да, начальный индекс 0 убьет ваш код. Кстати, в FORTRAN 90 есть функция перераспределения, но в качестве аргумента она принимает указатель. Наверное, не хочу туда за этим приложением.   -  person bob.sacamento    schedule 12.10.2012


Ответы (4)


так что это, вероятно, ужасная форма, и, безусловно, плохое время выполнения (он копирует массив для каждого бита), но вот что я придумал. Кажется, это работает.

  program test
      implicit none
      integer, dimension(:), allocatable :: binary
      integer :: decimalnum, i, initvalue, curSize, curBit


      print*, "Enter decimal number to convert: "
      read*,initvalue

      decimalnum = initvalue
      i = 1
      ALLOCATE ( binary(1) )
      curSize = 1

      DO WHILE (decimalnum > 0)
        IF (i > curSize ) THEN
            curSize = curSize * 2
            CALL expandArray( curSize, i-1 )
        END IF

        IF (MOD(decimalnum,2)==0) then
            binary(i) = 0                  ! this is as far as the program executes up to 
            decimalnum = decimalnum / 2
            i = i + 1
        ELSE IF (MOD(decimalnum,2)==1) then
            binary(i) = 1
            decimalnum = (decimalnum -1) / 2
            i = i + 1
        END IF

      end do
      PRINT*, binary


  CONTAINS
      SUBROUTINE expandArray( newSize, oldSize )
          IMPLICIT NONE
          INTEGER, DIMENSION(:), ALLOCATABLE :: temp
          INTEGER :: j, newSize, oldSize
          ALLOCATE( temp(newSize) )
          DO j=1,oldSize
              temp(j) = binary(j)
          END DO
          DEALLOCATE (binary)
          ALLOCATE( binary(newSize) )
          DO j=1,oldSize
              binary(j) = temp(j)
          END DO
          DO j=oldSize+1,newSize
              binary(j) = 0
          END DO
          DEALLOCATE (temp)
      END SUBROUTINE

  END PROGRAM test
person Nathan Andrew Mullenax    schedule 08.10.2012
comment
Примечание: только что отредактировано - я ошибочно использовал двоичный файл в качестве параметра для expandArray вместо глобальной переменной. Почему-то все равно работало. В любом случае, сейчас это исправлено. - person Nathan Andrew Mullenax; 08.10.2012
comment
Я согласен с автором, этот код, вероятно, ужасен и копирует массив для каждого бита. Он использует функции (в частности, операторы continue) FORTRAN77, которые были заменены более совершенными средствами. - person High Performance Mark; 08.10.2012
comment
@HighPerformanceMark Попытался немного привести его в порядок - лучшее перераспределение массива, избавился от do/continue. Очевидно, я все еще не программист на Фортране, поэтому правки/отзывы от профессионала будут очень признательны. - person Nathan Andrew Mullenax; 08.10.2012

Вот простой способ преобразовать целое число i в его двоичное представление:

write(*,'(b16)') i

Как написано, это не будет записывать никаких ведущих 0. Если вам нужны ведущие 0, попробуйте

write(*,'(b16.16)') i

Конечно, предыдущий код записывает двоичное представление в модуль вывода по умолчанию, но, используя внутренние возможности записи Фортрана, я мог бы так же легко записать биты в символьную переменную. Например:

character(len=16) :: bits
...
write(bits,'(b16.16)') i

записывает двоичные цифры i в символьную переменную bits.

Теперь, если вы действительно хотите создать массив целых чисел, каждое из которых представляет один бит двоичного представления, то что-то вроде этого

integer, dimension(16) :: bitarray
...
bitarray = 0
...
do ix = 1,len(bits)
    if (bits(ix:ix)=='1') bitarray(ix) = 1
end do

наверное сработало бы.

person High Performance Mark    schedule 08.10.2012

1) Ваш сбой происходит из-за того, что вы выделили только 1 элемент для двоичного массива (:), и цикл while, предположительно, перешел к i = 2, и в этот момент ваш массив выходит за пределы индекса (сбой).

2) Фортран имеет ряд встроенных функций, которые напрямую работают с битами. Например,

а) Bit_Size(var) возвращает количество битов в «var», поэтому, если вы должны использовать выделяемый массив, теперь вы знаете требуемый размер массива заранее.

б) BTest(iVar, pos) возвращает .True. если бит pos в iVar равен 1

Например, используя другие объявления выше:

Integer     :: n

n = Bit_Size(decimalnum)    

If( Allocated(Binary) ) DeAllocate(Binary)          ! good practice
Allocate( Binary(1:n) )                             ! in general, should get IOStat, just in case

Binary(:) = 0

ForAll(i=1:n, Mask = BTest( decimalnum, i-1 ) ) ! remember, bit intrinsics index from 0
    Binary(i) = 1
End ForAll

... это немного более эффективно по сравнению с Do and While и может помочь (немного) в отношении smp. Можно также использовать конструкцию Where/EndWhere, но я считаю, что ForAll немного более эффективна.

c) IBits(var, pos, len) извлекает биты из var, начиная с pos, для количества битов len, например, если вы хотите создать «явное» двоичное представление, это может быть одним из способов.

и т. д. и т. д.

3) Если вы «действительно имеете в виду» преобразование DecimalNum в Bin, то у вас есть (существенные) дополнительные проблемы, если Dec также включает Dec с плавающей запятой (т.е. Reals), поскольку битовое представление Reals выражается в показателях степени. Я предполагаю, что это не так, поскольку код/объяснение для этого гораздо сложнее.

Наконец, в Фортране числа обычно являются «знаковыми» числами, а начальный бит используется для определения +ve (0) или -ve (1). Поэтому, если вы шли в «другом» направлении (Bin2Dec), то предпочли бы дополнительный аргумент (возможно, необязательный), который контролирует, является ли результат подписанным или беззнаковым. Если без знака, то выходная переменная должна быть "больше" по сравнению с входной переменной (например, при преобразовании беззнакового 1-байтового целого числа в Fortran int необходимо использовать как минимум 2-байтовое целое число (т.е. входное целое число(1) должно быть вывод в Integer(2)) и т.д.

person DrOli    schedule 26.06.2014

Согласно комментариям, вам нужно выполнить оператор выделения (или что-то, что делает выделение для вас под прикрытием), прежде чем вы сможете определить массив binary. Простейшая форма оператора распределения будет выглядеть примерно так: ALLOCATE(binary(10)), что даст массиву binary 10 элементов, используя по умолчанию (его можно изменить для этого массива с помощью оператора allocate) начальный индекс массива, равный 1.

Если размер выделения не так легко узнать до начала работы с массивом, есть два основных подхода:

  • Выполните два прохода, первый проход которых просто подсчитывает, сколько элементов требуется, затем выделяется массив, затем второй проход фактически выполняет присвоение соответствующим элементам.
  • Выделите массив до начального размера (который может быть равен нулю), постепенно увеличивая массив по мере необходимости.

Существуют компромиссы, связанные с решением относительно подхода к использованию, связанные с относительными накладными расходами, такими как распределение и оценка каждого теста при подсчете.

В Fortran 90 (пора переходить как минимум на Fortran 95!), увеличение выделяемого массива несколько запутано (выделить временный массив, скопировать данные из исходного во временный, освободить исходный, выделить исходный в новый размер, скопировать данные из временного обратно во временный). оригинал с измененным размером, временное освобождение). В Fortran 2003 эта операция становится тривиальной.

person IanH    schedule 08.10.2012
comment
@AussieGamer: вам будет сложно найти компилятор, который принимает большую часть дополнительного синтаксиса, добавленного в версии языка 2008 года; большинство компиляторов на рынке в настоящее время реализуют большую часть Fortran 2003. - person High Performance Mark; 08.10.2012