Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Orlando_R_
Beginner
362 Views

ifort might return an incorrect C_BOOL .true. in iso_c_binding

Hi Forum members

Following code consisting of a fortran function  and  client in c,   is giving the wrong result

ifort  -std03  foo.f90:

module foo

contains

    function bar() bind( c )

      use, intrinsic :: iso_c_binding
      implicit none
      logical(kind=c_bool) :: bar

      bar = .true.

    end function bar
end module foo

icc -std=c99 main.c

#include <stdio.h>
#include <stdbool.h>

extern bool bar();

int main()
{
  bool t;

  t = bar();

  printf( "t: %i\n", t );
  printf( "not t: %i\n\n", ! t );

  if ( t == true )
    printf( "t is true\n" );
  else
    printf( "SURPRISE: t is NOT true\n" );

  return 0;
}

Compiling with gcc/gfortran (4.7+) the return value of  bar() is  .true.(1) , Therefore,   the value of t is also true (1). 

Compiling with ifort/icc (Composer 2015/13)   bar in foo.f90 is .true. (-1), but  t has a value of (255), which is not true

According with  the ISO/IEC JTC1/SC22/WG5 N1601 Draft of the Fortran 2003 STD (page 390)

"The value of C BOOL shall be a valid value for a logical kind parameter on the processor or shall be -1."

Pages below in the description  page 395 table 15.2 , the fortran type Logical (kind=C_BOOL), should  match _Bool in c.  Which in turn is bool (see include/stdbool.h )  and  it also  defines  true=1 and false=0.  These values are given with gcc and are also decribed in the section 5.2 of the gfortran info page

5.2 Internal representation of LOGICAL variables
================================================

The Fortran standard does not specify how variables of `LOGICAL' type are represented, beyond requiring that `LOGICAL' variables of default
kind have the same storage size as default `INTEGER' and `REAL' variables.  The GNU Fortran internal representation is as follows.

   A `LOGICAL(KIND=N)' variable is represented as an `INTEGER(KIND=N)' variable, however, with only two permissible values: `1' for `.TRUE.'  and `0' for `.FALSE.'. Any other integer value results in undefined behavior.

   Note that for mixed-language programming using the `ISO_C_BINDING'  feature, there is a `C_BOOL' kind that can be used to create
`LOGICAL(KIND=C_BOOL)' variables which are interoperable with the C99 _Bool type.  The C99 _Bool type has an internal representation
described in the C99 standard, which is identical to the above description, i.e. with 1 for true and 0 for false being the only
permissible values.  Thus the internal representation of `LOGICAL' variables in GNU Fortran is identical to C99 _Bool, except for a
possible difference in storage size depending on the kind.

Why  ifort is not returning a compatible .true.=true (c )  when using ifort  ?   thanks

Best Regards

Orlando

 

 

 

 

 

 

0 Kudos
8 Replies
jimdempseyatthecove
Black Belt
362 Views

The function bar, which has a calling convention of bind(C), is not exclusively callable by C (C++). IOW it is just as likely callable by Fortran.

The return "bar" is declared as type LOGICAL with the size of that of a c_bool (c int). In all respects the return "bar" is a Fortran LOGICAL.

If you want the function bar to be only callable by C, and return the C equivalent of true or false, then declare "bar" as INTEGER(kind=c_bool) and return 1 or 0 as the case may be. But please note that should the reformulated function be called by Fortran, then expect the following quirks.

The return value (to Fortran) may be copied into a Fortran LOGICAL and/or used directly in an outer scoping IF(expression). Being more familiar with C, you might be inclined to use "==" and "!=" for making comparison tests on LOGICAL's. This is not technically correct in Fortran. Fortran has .EQV. and .NEQV. for expressly testing LOGICALS. These test only the least significant bit position of each LOGICAL, whereas the "==" and "!=", depending on implementation, may treat (cast) the LOGICALS as INTEGER, and you will end up with the 1 == -1 which fails on misinterpretation of .true.

The Fortran side should use  .EQV. and .NEQV. and the C side should use a conversion function (inline) to convert to C format (x&1).

The other option you have is for interoperational purposes, do not use "if(bar() == true)", instead use "if(bar())" or "if(bar() != false)".
(and comment why you are doing so).

Jim Dempsey

 

jimdempseyatthecove
Black Belt
362 Views

Be careful in that "if(bar())" interprets non-0 as true, whereas Fortran interprets the least significant bit as indicator of .true. or .false. thus -2 in Fortran would be interpreted as .eqv. to .false.

Jim Dempsey

JVanB
Valued Contributor II
362 Views

You need the switch -fpscomp logicals to get the behavior that semi-matches gcc. There is a bug in most Fortran compilers in that they return C_BOOL /= -1, and ifort is one of such buggy compilers. The matching type, _Bool, is an oddball underspecified type even in C99, and it pretty much breaks LOGICAL variables in Fortran when the compiler tries to match it. My recommendation is to forget about trying to interoperate with _Bool and instead use int on the C side just like all actual C programs do. On the Fortran side the appropriate type is INTEGER(C_INT) and you can check whether it is zero or not just as C programs do.

 

FortranFan
Honored Contributor II
362 Views

You indicate, "icc -std=c90 main.c"  I guess you meant -std=c99; https://software.intel.com/en-us/node/582029.

Is the typo just here, or also in the actual command you used?  I'm not familiar with icc; don't know what happens if -std= has an unaccepted value - does it go to default or stop?

Anyways, assuming command is correct, would the following make a difference?

bar = logical( .true., kind=c_bool )

Can't reproduce the error on Windows with Microsoft C compiler and the following:

module m

   use, intrinsic :: iso_c_binding, only : c_bool, c_double

   implicit none

contains

   function bar( rnum ) bind( c, name="bar" )

      real(kind=c_double), intent(in) :: rnum

      logical(kind=c_bool) :: bar

      if (rnum > 0.5_c_double) then
         bar = logical( .true., kind=c_bool )
      else
         bar = logical( .false., kind=c_bool )
      end if

      return

   end function bar

   function rn() bind( c, name="rn" )

      real(kind=c_double) :: rn

      call random_number( rn )

      return

   end function rn

end module m
#include <stdio.h>
#include <stdbool.h>

extern bool bar( double * );
extern double rn();

int main()
{
   double rnum;
   bool t;
   bool r;

   for (int i=0; i < 1000; i++ )
   {
     rnum = rn();
     r = rnum > 0.5;
     t = bar( &rnum );

     if ( t != r ) {
      printf( "SURPRISE: t is NOT correct.\n" );
      printf( "rnum: %f\n", rnum );
      printf("%s\n", r ? "true" : "false");
     }

   }

  return 0;
}

 

Orlando_R_
Beginner
362 Views

Yeah, it was a typo,

thanks for you Inputs.  Anyways many  workarouds  will work,  but ,still,  using iso_c_binding  kind=c_bool should't  be interchangeable  with the correspoding bool in c?    As repeat Offencer says in this case  is better to use an int ,  but  Jim made a good point too,   bar() might be well called by a fortran code .

 

 

 

FortranFan
Honored Contributor II
362 Views

RO alluded to faulty compilers, but could the fault be on the icc side though in its implementation of the C99 standard with the addition of bool type or the use of -std=c99 compiler option, rather than in ifort?  Perhaps someone from Intel Fortran team can work in sync with the C++ compiler team to check this out?

TimP
Black Belt
362 Views

Steve Lionel has commented on this subject on these forums before.  As RO pointed out above, ifort requires the option -fpscomp logicals (included in -standard-semantics) in order to comply with the current Fortran standard on inter-operability of c_bool.  As you pointed out, unless you set -std=c99 (or later), any support for _Bool in icc is as an extension.  interoperability between the extension bool and _Bool also is a non-standard extension (which I would expect to work OK in icc).

One of the tricky things there is that current gcc has a default of -std=gnu99 (C99 plus extensions) and setting -std=c99 for both icc and gcc should improve their compatibility (unless it requires _Bool rather than bool), but Intel C++ in C mode also tries to be somewhat compatible with Microsoft C, which supports only C89 (with some breakage in VS2015 online updates).  I don't know whether gcc 4.7 had adopted the gnu99 default or remained at gnu89.

You quoted from some version of gfortran docs (4.7 is fairly old now) in your original post, seemingly from before the Fortran standard set a requirement on the representation of logical; however, gfortran always adhered to a C compatible representation, so you would have needed ifort -fpscomp logicals in order for ifort to work similarly with a C compiler.

The points about using c_int if you need compatibility with C89 are valid, but this has the possible pitfall of the need to set ifort -stand to stop you from making errors by mixing logical and integer, when you would still need -fpscomp logicals if you exploited the ifort loophole.

jimdempseyatthecove
Black Belt
362 Views

As an observation, perhaps it should be considered to provide two conversion functions analogous to C_F_POINTER.

(untested code)

FUNCTION C_F_BOOL(CBOOL)
  LOGICAL :: C_F_BOOL
  INTEGER(kind=C_BOOL) :: CBOOL
  C_F_BOOL = (CBOOL != 0) ! or ((CBOOL == 1) and an assert for not 0 nor 1
END FUNCTION C_F_BOOL

FUNCTION F_C_BOOL(FBOOL)
  INTEGER(kind=C_BOOL) :: F_C_BOOL
  LOGICAL :: FBOOL
  F_C_BOOL = IAND(FBOOL.1)
END FUNCTION F_C_BOOL

Jim Dempsey

Reply