sponsored links



No announcement yet.

%EDITC with Negative numbers > RNX0105

  • Filter
  • Time
  • Show
Clear All
new posts

  • %EDITC with Negative numbers > RNX0105

    Hi, everyone. I may have a "can't get there from here" problem with the way %EDITC works using edit code 'X' with negative values.

    I'm converting old code with LOTS of (dreaded) MOVE, MOVEL, and MOVEA instructions. We have ARCAD-Transformer RPG (with which I'm pretty happy) to aid in conversion to Free-format ... but it's only human. ;-) The program contains three fields:
    RPAG   : PACKED(15: 0)
    SH#HGA : CHAR(15)
    $H2    : PACKED(15: 0)
    Our old code looks like this (and you gotta love those effortless character-numeric conversions):
    C                   MOVE      RPAG          SH#HGA
    C                   MOVE      SH#HGA        $H2              15 0
    ... and the free-format conversion looks like this:
    SH#HGA = %EDITC(RPAG:'X');
    $H2 = %DEC(%XLATE(' ':'0':SH#HGA):15:0);
    ... which seems to me a pretty decent and safe code conversion.

    However, in testing, RPAG receives the negative value -46100. This causes no problem with the compiled fixed-format code, but in free-format code, "%EDITC(RPAG:'X')" produces "00000000004610}", as the negative sign is indicated by a 'D' in the high-order bits of the last digit (X'D0"="}"). Then when the %DEC is encountered, expecting a character string that can be interpreted as numbers, the last character of SH#HGA causes runtime error RNX0105 (A character representation of a numeric value is in error).

    Without going in to too much gory detail about how MOVE works, the use of %EDITC(xxx:'X'), rather than %CHAR(xxx), is almost mandatory, since %CHAR (as well as edit codes other than 'X') truncates leading zeroes and therefore produces strings of different lengths, depending upon content. OTOH, %EDITC( :'X') produces a character string of a consistent length.

    So while %CHAR and %DEC seem to be inverse functions, %EDITC( :'X') and %DEC are not. (To be fair, I'm not sure what %EDITC( :'X'') could/should do with a negative value other than what it's doing now.)

    To avoid the problem, we may do something like this:
    IF RPAG >= 0;
      SH#HGA = %EDITC(RPAG:'X');
      SH#HGA = '-' + %SUBST(%EDITC(- RPAG:'X'):2);
      // at the risk of losing a high-order non-zero digit.
    ... or a similar workaround, which is simple enough but requires manually seeking out, analyzing, and fixing every numeric-to-character MOVE statement.

    Can anyone suggest how to mitigate the problem? :-) Any magical compiler settings, CTL-specs, ARCAD parameter settings, anything?

  • #2
    What is the intent of the original code, to take the absolute value of RPAG? If so can't you just $H2 = %abs(RPAG);


    • #3
      Hi, Scott; thanks for the reply. I don' think %ABS works for us here, as the sign is still needed, despite the software shuffling values through character fields..(We're using software which actually includes a routine that tests for "}" and J-R in the output string, changes the zone value from "D" to "F", and attaches a "-". Too bad %DEC doesn't do that.) ;-)


      • #4
        Like Scott M I'm having trouble understanding what the heck the original code was supposed to do.

        First - have you reported the error to Arcad? If not you should.

        Your work around appears to be moving the sign to the front end - which a MOVE wouldn't have done so I'm confused by that.

        Using an edit code of Q will give you a leading sign result that is accepted by %Dec but the sign is floated onto the first significant digit - so no leading zeros. Also it won't work pif the input is zero - a "defect" I hope %Dec will fix soon. There will be problems if the field has a significant digit in the left-most position.

        Using %EditW might work better for you - but you'll lose the _least_ significant digit unless the target field is one character longer than the source i.e. if the packed is 15 long the char must be 16. This is because the sign prefixes the string.

        This is the code I played around with:
               dcl-s result char(16);
               dcl-s input  packed(15) inz(-12345);
               dcl-s  wait  char(1);
               // I would have to throw myself off a cliff if I actually ever did this but ...
             c                   move      input         result
               Dsply ('How we doing so far? ' + result) ' ' wait;
               result = %EditC(input:'Q');
               Dsply ('How we doing so far? ' + result) ' ' wait;
               result = %EditW(input:'0-              ');
               Dsply ('How we doing so far? ' + result) ' ' wait;
               Dsply %Dec(result:15:0) ' ' wait;
              *InLr = *On;


        • #5
          Originally posted by Jerry G View Post
          Hi, Scott; thanks for the reply. I don' think %ABS works for us here, as the sign is still needed, despite the software shuffling values through character fields..(We're using software which actually includes a routine that tests for "}" and J-R in the output string, changes the zone value from "D" to "F", and attaches a "-". Too bad %DEC doesn't do that.) ;-)
          So whoever wrote it din't understand that 'D' and 'F' were equivalnt. Hmmmm.


          • #6
            Unfortunately, MOVE doesn't actually convert a number to text. It simply puts it in zoned decimal format (which isn't really text, though for positive numbers in EBCDIC, its pretty close) and puts it in a character field.

            This was the way that MOVE did thing, which was really awful. Back in the days when MOVE was our only real choice for converting numbers (we're talking early 1990s or earlier) it was really difficult to covert numbers to something that could be read by other software such as database imports, excel spreadsheets, or imports into other applications because basically nothing understands the zoned decimal format that MOVE outputs. (There are a few things, basically all IBM-related stuff... but trying to go to anything non-IBM was a nightmare.)

            It sounds like you either found software (or more likely, someone custom wrote something) that actually understands that zoned format. So now you're stuck with it, even though basically everyone (including free format RPG) has abandoned it. Sorry to hear that. If I were you, my first choice would be to update that software to read proper text-based numbers. So something like ' -1234' instead of '000123M', etc.

            But assuming that's not possible (at least in the short term) it seems like your only real problem is %DEC. You see, %DEC is expecting text data as input -- it's not expecting a zoned decimal number. (There has been a lot of talk about %EDITC( num:'X'), and this doesn't seem to be a problem since that would produce output identical to a MOVE.. i.e. a zoned number.) So you have this weird workaround that sort-of makes it into a text field (albeit with leading zeroes... ick) by manually concatenating a dash to the front of it when its negative.

            Perhaps it'd be easier to go back to dealing with the horrible zoned format and simply using a data structure rather than %DEC? At least until you can upgrade the rest of the software to allow proper text values?


            • #7
              Thanks everyone. To offer a brief explanation: The base of our ERP system is J.D.Edwards code, parts of which date to the 1980s. As things like field sizes and decimal positions could vary among potential clients, all fields on display panels are character. Panels are read and written through utility subroutines that reference a lookup table to "translate" character strings to numeric file fields. To handle lots of customers' differing needs, this was kind of slick but as the guy who has to convert this to free-format, the words I mutter to myself about it are not quite flattering.

              Scott wrote, "This was the way that MOVE did thing, which was really awful." Amen! It may have been a bit controversial at the time, but I definitely understand why the MOVE instructions (along with GOTO and TAG) were jettisoned in defining free-format RPG.

              Now, to be less brief (because I'm a bit OCD, I've already documented this for my successor(s), and maybe this might help someone else):

              Because of the way the MOVE instructions work (unlike EVAL or Z-ADD), the ARCAD conversion software takes great pains to move only the same number of bytes as would be copied by the MOVE instruction it's replacing. Some of it is done by arithmetic manipulation (like multiplying or dividing by numbers like 100000000), but much of it is done via character manipulation, converting between character and numeric data and using %SUBST and concatenation. Considering how precise you can be using all the nice character functions, this makes sense. But, then you get stuff like ...
                    *    Fixed format, #ALNUM is CHAR(22):
              (1)  C                   MOVE      #ALNUM        #WK29            29 0 
                     // becomes:
              (2)      #WK29 = %DEC(%SUBST(%EDITC(#WK29:'X'):1:7) + %XLATE(' ':'0':#ALNUM) :29:0);
                     //    MOVE would not zero-out the leftmost 7 bytes of #WK29
              ... and ...
              (3)  C                   MOVE      #WK29         $NBR3             3 0
                     // becomes:
              (4)      IF #WK29 >= 0;
                           $NBR3 =   %DEC(%SUBST(%EDITC(  #WK29:'X'):27:3):3:0);
                           $NBR3 = - %DEC(%SUBST(%EDITC(- #WK29:'X'):27:3):3:0);
              Because the MOVE instructions operate on the basis of the operands' defined length (not the length of their current content), %EDITC(num:'X') (rather than, say, %EDITC(num:'L')) is heavily used to convert numbers to characters. Where a numeric value is being moved to another numeric value (3), the converted code tests the sign of the source operand, and deals with it appropriately. When an intermediate value (like the character result of %EDITC(#WK29:'X')) is needed elsewhere (such as for display), then we have trouble with negative numbers. In using the BIFs in free-format, the problem comes from "what to do with the sign"; %EDITC(#WK29:'X') "hides" it as a x'D' in the zone field of a numeric character, but unfortunately there seems to be no inverse function to pick up that zone when converting the character string back to numeric.

              I'll just have to deal with it ;-) ... but I've wondered how this might be addressed:
              Add a new parameter to %DEC to consider the zone field in the last character, so a final "digit" of "}"/"J"/"K"/.../"R" is acceptable as a representation of a negative number?
              Add a new edit code, much like 'X', to maintain the length of a field in the conversion (or maybe LEN + 1?), allowing the sign to be explicitly kept in a position/format acceptable to %DEC?
              Other ideas?

              Thanks again, folks, for the opportunity pick your (massive) brains! ;-)


              • #8
                Originally posted by JonBoy View Post

                So whoever wrote it din't understand that 'D' and 'F' were equivalnt. Hmmmm.
                D and F aren't equivalent. B and D are equivalent (negative) and A, C, E and F are equivalent (positive).

                This whole discussion is a blast from the past, A?

                Jerry, I would use data structures with zoned subfields for this type of conversion where the "character" form was supposed to have the sign embedded in the last character, '1234M' etc. For each conversion like this, create a data structure with a character subfield and a zoned subfield of the required length (or several zoned subfields if you sometimes have non-zero decimal positions to deal with). Assign the zoned value to the zoned subfield, and use the character subfield, or assign to the character subfield and use the zoned subfield.

                dcl-ds cvtZoned_15 qualified;
                   a char(15) pos(1);
                   z0 zoned(15:0) pos(1);
                   z9 zoned(15:9) pos(1);  // add as many subfields with different decimal positions as you need
                cvtZoned.z0 = RPAG;
                sH#HGA = cvtZoned.a;
                cvtZoned.a = SH#HGA;
                $H2 = cvtZoned.z0;


                • Jerry G
                  Jerry G commented
                  Editing a comment
                  Thank you, Barbara!

              • #9
                Yup - realized that yesterday but didn't get round to editing it. Thanks for clarifying.

                Which brings me to a question Barbara. I was looking for a solution via the MI built-ins to see if duplicating the old RPG400 behaviour could be achieved that way. But those MI operations (CVTNC and CVTCN for example) are not surfaced it appears although others from the same "family" are. Are they available but just not documented or do you know of a reason why they were omitted. They seem kinda useful.
                Last edited by JonBoy; 1 day ago.


                sponsored links