Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Initialization of int64 parameters with boz-literal-constants

jme
Beginner
1,538 Views

Hello,

If, in the body of a program, in a call to the INT intrinsic with second argument kind=int64, I use as first argument a boz-literal-constant such that either (A) the bit #31 is the highest bit specified and has value one (rightmost bit is #0) or (B) the boz constant is an octal constant where the two highest bit specified are #32 set to zero and #31 set to one (that is, the most significant octal digit is a 2 or a 3), I obtain the result I would expect according to the 2008 version of the Fortran standard (section 13.3.3):

"When a boz-literal-constant is the argument A of the intrinsic function INT or REAL, if the length of the sequence of bits specified by A is less than the size in bits of a scalar variable of the same type and kind type parameter as the result, the boz-literal-constant is treated as if it were extended to the length equal to the size in bits of the result by padding on the left with zero bits [...],"

 and the result is a positive integer number; e.g.,

   integer(kind=int64) :: Vz
             !76543210
   Vz = int(z'80000000', kind=int64)
   write(*,'(a, 24x,    a)') "Position", "76543210"
   write(*,'(a,i18,1x,z16)') "Vz = ", Vz, Vz

results in

Position                        76543210
Vz =         2147483648         80000000

However, if this happens when setting the value of a parameter, the boz-literal-constant seems to be padded with ones to the left: the code

                                               !76543210
   integer(kind=int64), parameter :: Pz = int(z'80000000', kind=int64)
   write(*,'(a, 24x,    a)') "Position", "76543210"
   write(*,'(a,i18,1x,z16)') "Pz = ", Pz, Pz

results in a negative integer

Position                        76543210
Pz =        -2147483648 FFFFFFFF80000000

I initially thought I might be interpreting incorrectly the standard, and that this behaviour could be related to the last sentence in the Result Value section of the INT intrinsic (section 13.7.81):

"The interpretation of a bit sequence whose most significant bit is 1 is processor dependent."

However, (a) I think this only makes sense if the most significant bit is 1 after the padding/truncation described in 13.3.3 has taken place, and (b) this would not include the octal case described in (B) above, since in that case the most significant bit specified is indeed zero.

Would it be possible for you to clarify whether my interpretation of the standard is wrong, or whether there might be a bug in the compiler?

I am experiencing this behaviour with the Intel compiler versions 16.0.3, 17.0.2 and 18.0.0 20170510.  I am attaching a source file that exemplifies this - the outputs I obtain with the three versions above are the same:

$ ifort minbozint.f90 -o minbozint.ifort -check all -warn all -std08 && ./minbozint.ifort
Position                        76543210
Pz =        -2147483648 FFFFFFFF80000000
Po =        -2147483648 FFFFFFFF80000000
Pb =        -2147483648 FFFFFFFF80000000
Vz =         2147483648         80000000
Vo =         2147483648         80000000
Vb =         2147483648         80000000

Thank you very much for your help.

0 Kudos
1 Solution
Steve_Lionel
Honored Contributor III
1,538 Views

Whichever choice is correct, the compiler ought to be consistent about it. My reading of the standard is that the value should be padded with zeroes. That it is not is a compiler bug.

That last sentence about "processor-dependent" is referring to the final value of A, after padding or truncation (if any). There's nothing in the standard that says a leftmost bit of 1 means a negative value - the standard goes out of its way to discuss the "bit model" (13.3) in terms of "non-negative" values. The sentence is not relevant to HOW the compiler does its padding or truncation.

View solution in original post

0 Kudos
16 Replies
Steve_Lionel
Honored Contributor III
1,539 Views

Whichever choice is correct, the compiler ought to be consistent about it. My reading of the standard is that the value should be padded with zeroes. That it is not is a compiler bug.

That last sentence about "processor-dependent" is referring to the final value of A, after padding or truncation (if any). There's nothing in the standard that says a leftmost bit of 1 means a negative value - the standard goes out of its way to discuss the "bit model" (13.3) in terms of "non-negative" values. The sentence is not relevant to HOW the compiler does its padding or truncation.

0 Kudos
Devorah_H_Intel
Moderator
1,538 Views

Thank you for your report! This issue is better to be reported via our Online Service Center at https://supporttickets.intel.com/  
Instructions on how to file a ticket are available here: 
https://software.intel.com/en-us/articles/how-to-create-a-support-request-at-online-service-center  

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,538 Views

z'80000000'

Is interpreted as INTEGER(4)

int(z'80000000', kind=int64)

Converts an INTEGER(4) to INTEGER(8)... with sign propagation.

use z'0000000080000000'

or try z'080000000'

thus forcing the literal to 36 bits (IOW into 64-bit internal form).

Jim Dempsey

0 Kudos
Steve_Lionel
Honored Contributor III
1,538 Views

It should not be doing sign propagation.

0 Kudos
jme
Beginner
1,538 Views

Dear All,

Thank you for your replies.

Steve: Thank you for your clarifications regarding the interpretation of the standard - and congratulations on your election as convenor of WG5!

Jim: Thank you, there are a number of workarounds that I could use, but that wasn't what I was asking for. I think that what you say about interpretations and conversions is not correct: boz-literal-constants such as z'80000000' are not interpreted standalone, they are only valid in a few situations specified by the standard, and their interpretation has to follow section 13.3.  According to the standard, there is no "interpretation as integer(kind=int32) and then conversion to integer(kind=int64)", there's a direct interpretation as integer(kind=int64) after the padding with zeroes described above has taken place.  If the compiler was doing a conversion from int32 to int64, that would not be in agreement with the standard and would be the source of the issue here reported.

Kind regards,

0 Kudos
JVanB
Valued Contributor II
1,538 Views

I was going to say that Quote #4 makes no sense, but it is kinda consistent with test programs for ifort 16.0. Consider:

module M
   use ISO_FORTRAN_ENV
   implicit none
   interface bitseq
      module procedure bs1,bs2,bs4,bs8
   end interface bitseq
   contains
      subroutine bs1(X)
         implicit none
         integer(INT8), intent(IN) :: X
         write(*,'(3(g0),Z0)') 'KIND = ',KIND(X),', X = ',X
      end subroutine bs1
      subroutine bs2(X)
         implicit none
         integer(INT16), intent(IN) :: X
         write(*,'(3(g0),Z0)') 'KIND = ',KIND(X),', X = ',X
      end subroutine bs2
      subroutine bs4(X)
         implicit none
         integer(INT32), intent(IN) :: X
         write(*,'(3(g0),Z0)') 'KIND = ',KIND(X),', X = ',X
      end subroutine bs4
      subroutine bs8(X)
         implicit none
         integer(INT64), intent(IN) :: X
         write(*,'(3(g0),Z0)') 'KIND = ',KIND(X),', X = ',X
      end subroutine bs8
end module M

program P
   use M
   implicit none
   call bitseq(Z'80000000')
   call bitseq(Z'080000000')
   call bitseq(Z'0000000080000000')
end program P

The output with both 32- and 64-bit compilers is:

KIND = 8, X = 80000000
KIND = 8, X = 80000000
KIND = 8, X = 80000000

So that in an ordinary expression, the KIND is 8 in all 3 cases. However:

program P
   implicit none
   integer, parameter :: K1 = KIND(Z'80000000')
   integer, parameter :: K2 = KIND(Z'080000000')
   integer, parameter :: K3 = KIND(Z'0000000080000000')
   write(*,'(*(g0))') 'K1 = ',K1
   write(*,'(*(g0))') 'K2 = ',K2
   write(*,'(*(g0))') 'K3 = ',K3
end program P

Yields the output:

K1 = 4
K2 = 8
K3 = 8

Which is consistent with the prediction of @jimdempseyatthecove. Now we have to ask: is the fact that the BOZ constant is used in an initialization expression or a specification expression that triggers the bug? Let's try an example with only a specification function:

module M
   use ISO_FORTRAN_ENV
   implicit none
   interface bitseq
      module procedure bs1,bs2,bs4,bs8
   end interface bitseq
   contains
      pure function bs1(X)
         implicit none
         integer bs1
         integer(INT8), intent(IN) :: X
         bs1 = KIND(X)
      end function bs1
      pure function bs2(X)
         implicit none
         integer bs2
         integer(INT16), intent(IN) :: X
         bs2 = KIND(X)
      end function bs2
      pure function bs4(X)
         implicit none
         integer bs4
         integer(INT32), intent(IN) :: X
         bs4 = KIND(X)
      end function bs4
      pure function bs8(X)
         implicit none
         integer bs8
         integer(INT64), intent(IN) :: X
         bs8 = KIND(X)
      end function bs8
      pure function F1(X)
         integer, intent(IN) :: X
         integer F1(bitseq(Z'80000000'))
         F1 = X
      end function F1
      pure function F2(X)
         integer, intent(IN) :: X
         integer F2(bitseq(Z'080000000'))
         F2 = X
      end function F2
      pure function F3(X)
         integer, intent(IN) :: X
         integer F3(bitseq(Z'0000000080000000'))
         F3 = X
      end function F3
end module M

program P
   use M
   implicit none
   write(*,'(*(g0))') 'SIZE(F1(0)) = ',SIZE(F1(0))
   write(*,'(*(g0))') 'SIZE(F2(0)) = ',SIZE(F2(0))
   write(*,'(*(g0))') 'SIZE(F3(0)) = ',SIZE(F3(0))
end program P

Output with ifort:

SIZE(F1(0)) = 8
SIZE(F2(0)) = 8
SIZE(F3(0)) = 8

So it's OK in just a specification expression. Curious that the bug requires such a specific trigger, and really has two components: one as a BOZ extension and one as standard BOZ usage.

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,538 Views

Steve,

Thanks, I should re-read docs re: INT:

If a is a binary, octal, or hexadecimal literal constant, the value of the result is the value whose bit sequence according to the model in Bit Model is the same as that of a as modified by padding or truncation according to the following:

If the length of the sequence of bits specified by a is less than the size in bits of a scalar variable of the same type and kind type parameter as the result, the binary, octal, or hexadecimal literal constant is treated as if it were extended to a length equal to the size in bits of the result by padding on the left with zero bits.

So it appears that in the case of an argument to INT, the (8-character) hex literal lost its hex-iness.

Here is an alternate test case to RO's:

program TestHex
    implicit none
    if(Z'80000000' .gt. 0) then
        print *, "gt 0"
    else
        print *, "lt 0"
    endif
    if(Z'80000000' .gt. 0_8) then
        print *, "gt 0_8"
    else
        print *, "lt 0_8"
    endif
    if(int(Z'80000000',kind=8) .gt. 0) then
        print *, "kind 8, gt 0"
    else
        print *, "kind 8, lt 0"
    endif
end program TestHex

 lt 0
 gt 0_8
 kind 8, gt 0

This illustrates that the interpretation is correct (for the above code example, and V17u1).

I can see how this bug passed inspection.

Jim Dempsey

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,538 Views

V17 is ok

program TestHex
    implicit none
    integer, parameter :: int64 = 8
    integer(kind=int64) :: Vz,Vx
              !76543210
    Vz = int(z'80000000', kind=int64)
    Vx = int(z'80000000', kind=int64) + 0_8
    write(*,'(a, 24x,    a)') "Position", "76543210"
    write(*,'(a,i18,1x,z16)') "Vz = ", Vz, Vz
    write(*,'(a,i18,1x,z16)') "Vx = ", Vx, Vx
end program TestHex

Position                        76543210
Vz =         2147483648         80000000
Vx =         2147483648         80000000

Jim Dempsey

0 Kudos
JVanB
Valued Contributor II
1,538 Views

I think you're missing what ifort is doing to assign the KIND of a BOZ literal in Quotes #8 and #9 above. In the first two cases of Quote #8, the BOZ literal, being one of the operands to a 2-operand operator, takes the KIND of the other operand, as per the ifort docs. In the last case of Quote #8 and in Quote #9, the standard usage of a BOZ literal is happening in the execution-part of the program, where ifort is doing the right thing. Put the same syntax in the specification-part of the program and I am pretty sure that it will fail, just as in Quote #1.

 

0 Kudos
Steve_Lionel
Honored Contributor III
1,538 Views

jme wrote:

Steve: Thank you for your clarifications regarding the interpretation of the standard - and congratulations on your election as convenor of WG5

That hasn't actually happened yet, and it is very likely that there will be no "election" in that no other candidates have volunteered! My nomination still needs to be approved by the US SC22 Steering Committee and then confirmed by the ISO committees.  One might say I "didn't duck fast enough" when asked, but in truth it was an honor I was pleased to be able to accept.

0 Kudos
JVanB
Valued Contributor II
1,538 Views

Ooh, here's another test I wanted to try but I don't have recent enough ifort. What output does this produce on the most recent version of ifort?

module M
   use ISO_FORTRAN_ENV
   implicit none
   contains
      subroutine bitseq(X)
         class(*) X
         select type(X)
            type is(integer(int8))
               write(*,'(*(g0))') 'KIND = ',KIND(X)
            type is(integer(int16))
               write(*,'(*(g0))') 'KIND = ',KIND(X)
            type is(integer(int32))
               write(*,'(*(g0))') 'KIND = ',KIND(X)
            type is(integer(int64))
               write(*,'(*(g0))') 'KIND = ',KIND(X)
         end select
      end subroutine bitseq
end module M

program P
   use M
   implicit none
   class(*), allocatable :: X
   X = Z'80000000'
   call bitseq(X)
   X = Z'080000000'
   call bitseq(X)
end program P

Replacing X = Z'80000000' with allocate(X,source=Z'80000000') causes an access violation on the ALLOCATE statement in ifort 16.0.

 

0 Kudos
TimP
Honored Contributor III
1,538 Views

KIND = 8
KIND = 8

Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R
) 64, Version 18.0.0.083 Beta Build 20170510

0 Kudos
FortranFan
Honored Contributor II
1,538 Views

jme wrote:

.. Jim: Thank you, there are a number of workarounds that I could use, but that wasn't what I was asking for. ..

@jme,

Please, pray tell which other workaround will you use?  Since the problem presented in the original post is within the context of a named constant, I thnk you are going to struggle coming up with even one other sensible workaround, let alone "a number of" them.

And since what is at stake is a compiler bug, it is largely meaningless what the standard says, especially in section 13.3, when it comes to developing workarounds.  Regardless of how the verbiage in the standard evolves, there is only ever an infinite array of bugs that separate a compiler implementation from the text and Jim's statement in Quote #4 appears closer to what may actually be going on in Intel Fortran implementation and his workaround seems to make best sense:

program p

   use, intrinsic :: iso_fortran_env, only : int64, compiler_version

   implicit none

   write (*,*) "Compiler Version: ", compiler_version()

   blk1: block
                                                  !76543210
      integer(kind=int64), parameter :: Pz = int(z'80000000', kind=int64)
      write (*,*) "Block 1:"
      write (*,'(a, 24x,    a)') "Position", "76543210"
      write (*,'(a,i18,1x,z16)') "Pz = ", Pz, Pz

   end block blk1

   blk2: block
                                                   !76543210
      integer(kind=int64), parameter :: Pz = int(z'080000000', kind=int64)
      write (*,*) "Block 2:"
      write (*,'(a, 24x,    a)') "Position", "76543210"
      write (*,'(a,i18,1x,z16)') "Pz = ", Pz, Pz

   end block blk2

   blk3: block
                                                          !76543210
      integer(kind=int64), parameter :: Pz = int(z'0000000080000000', kind=int64)
      write (*,*) "Block 3:"
      write (*,'(a, 24x,    a)') "Position", "76543210"
      write (*,'(a,i18,1x,z16)') "Pz = ", Pz, Pz

   end block blk3

   stop

end program p

Upon execution,

 Compiler Version:
 Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(

 R) 64, Version 18.0.0.083 Beta Build 20170510

 Block 1:
Position                        76543210
Pz =        -2147483648 FFFFFFFF80000000
 Block 2:
Position                        76543210
Pz =         2147483648         80000000
 Block 3:
Position                        76543210
Pz =         2147483648         80000000

 

If you have not done so, please follow up as suggested by Devorah in Quote #3 and report an incident at Intel's Online Service Center (OSC),, for only then is there some assurance Intel staff will follow up on the compiler bug.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,538 Views

FWIW, in my 50 years of programming I've experienced too many compiler errors to count. Once discovered, life must go on. i.e. when code must get finished - produce a work around, don't wait for a fix. The trick is (one gets better the more the do this) to "divine" a probable cause, test the premise, and construct a work around. With fortuitous luck, the work around will be non-intrusive as well as have no impact on performance.

In this case, do not be too picky about adding an otherwise unnecessary 0 to the literal.

Also node, that a well constructed fix will work when the bug is removed from the compiler.

I think jme's post was simply a disclosure of a bug, and not a plea for a work around.

Jim Dempsey

0 Kudos
jme
Beginner
1,538 Views

Ticket submitted.

I was not completely sure that this was a bug, but I thought it might be one, and I was mostly seeking confirmation.

I had noticed that adding one or more zeros fixed the issue (at least for the binary and hex constants), like Jim did, and I think this is the most elegant workaround.  However, in the end this is about initialising an integer, so you could just compute its value manually (in this case, initialise to 2147483648_int64), or INT(<boz-literal-constant with bit #31 set to zero>, kind=int64) + 2_int64**31_int64, etc.

0 Kudos
Devorah_H_Intel
Moderator
1,538 Views

jme wrote:

Ticket submitted.

Thank you! 

0 Kudos
Reply