Please consider the following simple example
program tselect implicit none character(10) :: buf buf=" " buf(1:1) = char(0) ; call getit buf(1:1) = ' ' ; call getit buf(1:1) = char(255) ; call getit stop contains subroutine getit character(1), parameter :: hex00 = char( 0) character(1), parameter :: hexff = char(255) integer, parameter :: first_is00 = 0 integer, parameter :: first_isff = 255 integer, parameter :: first_isunknown = -1 integer :: first_is select case ( buf(1:1) ) case ( hexff ); first_is = first_isff case ( hex00 ); first_is = first_is00 case default ; first_is = first_isunknown end select print *, "using select case, I get ", first_is if (buf(1:1)==hexff) then ; first_is = first_isff else if (buf(1:1)==hex00) then ; first_is = first_is00 else ; first_is = first_isunknown end if print *, "using if ladder , I get ", first_is end subroutine getit end program tselect
Compiling it with Intel Fortran compiler 19.0.3, it does not work correctly :
Intel(R) Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 18.104.22.168 Build 20190206 Copyright (C) 1985-2019 Intel Corporation. All rights reserved. using select case, I get 0 using if ladder , I get 0 using select case, I get -1 using if ladder , I get -1 using select case, I get -1 <<< using if ladder , I get 255
I tested some older version I still have at hand, and I found that the Version 22.214.171.124 Build 20171018 also gives wrong results, while the version 17 used to work fine:
Intel(R) Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 126.96.36.199 Build 20161005 Copyright (C) 1985-2016 Intel Corporation. All rights reserved. Intel(R) Fortran 17.0-1632 GNU ld version 188.8.131.52.2-5.48.el6 20100205 + ./a.out using select case, I get 0 using if ladder , I get 0 using select case, I get -1 using if ladder , I get -1 using select case, I get 255 <<<< using if ladder , I get 255
I do not know if, and why, for some reason, the SELECT CASE construct is not implemented exactly in the same way of the semantically equivalent IF-THEN-ELSE ladder, but I would expect the same results anyway.
I hope the previous (exact, I guess) behavior will be reinstated soon.
For the sake of readability, I made plenty of changes from IF/THEN construct to SELECT CASE construct in the legacy code I'm working on, expecially to avoid compound logical expression. And I'm afraid that using new (apparently wrong) versions of the compiler will break it.
I don't get any message even using the options
-stand f03 -warn all -check all
Please let me know if there is something wrong or not standard-compliant in my code.
Tanks a lot for your attention.
.. let me know if there is something .. not standard-compliant in my code ..
As suggested upthread, your request for support with Intel can get you best feedback in terms of Intel compiler.
However, my take is your code is in a grey area when it comes to the Fortran standard which says for CHAR intrinsic the input argument I "shall be of type integer with a value in the range 0 ≤ I ≤ n−1, where n is the number of characters in the collating sequence associated with the specified kind type parameter". Should a processor use base ASCII for its supported collating sequence, then the standard says "The collating sequence for the ASCII character kind is as specified in ISO/IEC 646:1991 (International Reference Version)". Now ISO/IEC 646:1991 "specifies a set of 128 control and graphic characters such as letters, digits and symbols with their coded representation." So with such a sequence, the actual argument to CHAR can be an integer i such that 0 <= i <= 127. You appear to be getting into the EXTENDED ASCII character set with your value of 255 in the offending situation. So Intel Fortran support staff can best inform you whether that would be part of its supported collating sequence for its character set.
That is actually a good point. The Intel documentation for Parallel Studio 19 mentions: "The ASCII character set contains characters with decimal values 0 through 127". That is for Linux and MAC OS X. On Windows that might be different. Unfortunately, the ifort 17 documentation is no longer online to cross-check.
Thanks for your remarks.
However, in the example I've used char() just for simplicity, because the original lines in my program were:
character(1) :: char0 = transfer(z'00', "a") character(1) :: charFF = transfer(z'FF', "a")
(older versions featured an EQUIVALENCEing between character an integers, which are no longer standard-compliant, so I removed them).
And there are two counterfacts; a weaker one: it used to work up to version 17, and it works using other compilers; and a much stronger one: if the problem is in the value used, how is that the direct comparison in the IF statement works, while the CASE() statement doesn't ?
Also: in case of a domain problem in the function CHAR, I would also expect at least a remark at compile time, given that the arguments are constants, or an error/warning at runtime ( having used -warn all -check all ).
However, you might be right, in some extent. According to the documentation, the hex value "FF" does not represent anything belonging to the domain of the "characters" in Intel Fortran Compiler for Linux, so, strictly speaking, any operation on such an entity might be regarded as undetermined and unreliable. In this case, should I expect that even the logical comparison will stop working in the future, as the CASE() did ?
Even reading the contents of a binary file into a character buffer? Sould be this the case (my example is a small part of a much more complex bynary file processing) I think the best think would be rewriting this part of the program in C. But this would be a huge and unworthy effort!
Why not use INTEGER(1), or the C-Interopt equivalent:
use, intrinsic :: iso_c_binding
integer(C_INT8_T) :: char0 = 0
integer(C_INT8_T) :: charFF = z'FF'
BTW this also requires you to re-type the characters in your READ and elsewhere. This is likely a lot easier to do than to port some code to C.
Thanks, Jim. I will ponder your suggestions (which look very reasonable) starting from an estimation of the impact this change would have on the whole related part of the program.
I knew that removing the non-standard EQUIVALENCEing in that spot was just touching the surface of the problem, and that being fully standard-compliant would have needed a more profound rewriting of the code. I'll try to find the most practical solution.
.. in the example I've used char() just for simplicity, because the original lines in my program were:.. character(1) :: charFF = transfer(z'FF', "a")
.. In this case, should I expect that even the logical comparison will stop working in the future, as the CASE() did ?
Even reading the contents of a binary file into a character buffer? Sould be this the case (my example is a small part of a much more complex bynary file processing) ..
Keep in mind constraint C7109 (R764) per Fortran 2018 standard states, "A boz-literal-constant shall appear only as a data-stmt-constant in a DATA statement, or where explicitly allowed in 16.9 as an actual argument of an intrinsic procedure."
Note SOURCE dummy argument (first parameter) in the intrinsic procedure TRANSFER is not marked as allowing a boz-literal-constant. Thus the original line in the program with TRANSFER technically does not conform to the Fortran standard. By the way, INT intrinsic in the standard for conversion to integer type explicitly states acceptance of a boz-literal-constant. You may be better off with using 32-bit integers for your data:
integer, parameter :: charFF = int( z"FF", kind=kind(charFF) )
You can then employ intrinsic procedures toward BITWISE manipulations with your data e.g., BTEST, IBITS, IBSET, etc.
But note in principle, the use of CHARACTER type to "hold" bits of data is alright, where you may run into issues is with determining the storage sizes, the use of other intrinsics with objects of such type and other operations like in the original post with CHAR and SELECT CASE (but again, do make support request to inform yourself on Intel Fortran team's views on the matter).
Re: "my example is a small part of a much more complex bynary file processing," can you elaborate on the broad sketches of your needs (you can omit any sensitive details)? Readers may then be able to comment on standard Fortran facilities toward your processing.
as for differences in v17 versus v18 and v19: we've got a bug report opened on this: After 17 a change was done that introduced a bug in comparisons for extended ASCII characters values 128-255 in SELECT CASE (maybe other comparisons too). Should get a fix in a future release that will get the v17 compiler behavior back.