Hello
I receive external data in string format and have to convert some decimal numbers represented as strings into real decimal types in COBOL.
In the following example I assume the decimal point to be "." (a dot) but this is not important.
I would like to know how to generically parse the following strings to decimals:
'1'
'0'
'0.1'
'1'
'123456789'
'123456789'
'123456789.987654321'
'123456789.987654321'
'0.1234567891'
'0.1234567891'
As far as I can understand, the use of NUMVAL does not allow me to detect if rounding happens when converting the number into a decimal, nor do I have the oppotunity to detect if truncation has happened on the left hand side of the decimal point in case of very large numbers.
Does anyone have a good solution for a general purpose string => decimal parsing which at least gives an error when the number is manipulated during the parse?
Thank you in advance
Topic

Re: Converting strings to decimal
20120912T12:51:34ZThis is the accepted answer. This is the accepted answer.Ha,
i use the following for receiving data from a web service:
01 WSAADIGIT PIC 9.
01 WSAADIVISOR PIC 9(5).
01 WSAAINDEX PIC S9(4) COMP3.
01 WRAAPD PIC 9(3)V9(5) EXTERNAL.
01 LSAAVALUE.
03 LSAAVALUELEN PIC S9(4) BINARY.
03 LSAAVALUEBODY PIC X(32767).
01 LSAAATTRS POINTER.
/
* We need to convert the character data 1234.56789 to 9(4)v9(5)
MOVE 0 TO WRAAPD
MOVE 1 TO WSAADIVISOR
PERFORM VARYING WSAAINDEX FROM 1 BY 1
UNTIL WSAAINDEX > LSAAVALUELEN
OR LSAAVALUEBODY(WSAAINDEX:1) = '.'
MOVE LSAAVALUEBODY(WSAAINDEX:1) TO WSAADIGIT
COMPUTE WRAAPD = WRAAPD * 10 + WSAADIGIT
ENDPERFORM
* Now the decimal portion, if any
ADD 1 TO WSAAINDEX
IF WSAAINDEX < LSAAVALUELEN
PERFORM VARYING WSAAINDEX
FROM WSAAINDEX BY 1
UNTIL WSAAINDEX > LSAAVALUELEN
OR LSAAVALUEBODY(WSAAINDEX:1) = ' '
MOVE LSAAVALUEBODY(WSAAINDEX:1) TO WSAADIGIT
COMPUTE WSAADIVISOR= WSAADIVISOR * 10
COMPUTE WRAAPD = WRAAPD +
(WSAADIGIT / WSAADIVISOR)
ENDPERFORM
ENDIF
ENDIF
I hope is helpful for you 
Re: Converting strings to decimal
20120912T13:01:07ZThis is the accepted answer. This is the accepted answer. nico1964
 20120912T12:51:34Z
Ha,
i use the following for receiving data from a web service:
01 WSAADIGIT PIC 9.
01 WSAADIVISOR PIC 9(5).
01 WSAAINDEX PIC S9(4) COMP3.
01 WRAAPD PIC 9(3)V9(5) EXTERNAL.
01 LSAAVALUE.
03 LSAAVALUELEN PIC S9(4) BINARY.
03 LSAAVALUEBODY PIC X(32767).
01 LSAAATTRS POINTER.
/
* We need to convert the character data 1234.56789 to 9(4)v9(5)
MOVE 0 TO WRAAPD
MOVE 1 TO WSAADIVISOR
PERFORM VARYING WSAAINDEX FROM 1 BY 1
UNTIL WSAAINDEX > LSAAVALUELEN
OR LSAAVALUEBODY(WSAAINDEX:1) = '.'
MOVE LSAAVALUEBODY(WSAAINDEX:1) TO WSAADIGIT
COMPUTE WRAAPD = WRAAPD * 10 + WSAADIGIT
ENDPERFORM
* Now the decimal portion, if any
ADD 1 TO WSAAINDEX
IF WSAAINDEX < LSAAVALUELEN
PERFORM VARYING WSAAINDEX
FROM WSAAINDEX BY 1
UNTIL WSAAINDEX > LSAAVALUELEN
OR LSAAVALUEBODY(WSAAINDEX:1) = ' '
MOVE LSAAVALUEBODY(WSAAINDEX:1) TO WSAADIGIT
COMPUTE WSAADIVISOR= WSAADIVISOR * 10
COMPUTE WRAAPD = WRAAPD +
(WSAADIGIT / WSAADIVISOR)
ENDPERFORM
ENDIF
ENDIF
I hope is helpful for you 
Re: Converting strings to decimal
20120925T10:56:29ZThis is the accepted answer. This is the accepted answer. SRYER
 20120913T06:27:54Z
I haven't run your code yet, but does it handle negative numbers? And overflows with too many characters?
I have used an output field of 8V8 to use your data for the "truncation" and then repeated with the truncated fields reduced by one or two digits as necessary.
Gets a warning on the compile, but since it is checking for truncation anyway, shouldn't be a problem. Can be avoided, of course, but since you've not specified the lengths you want to deal with, I can leave that.
. 01 WINPUTSTRING. 05 WINPUTSIGNIFPRESENT PIC X. 88 WINPUTHASSIGN VALUE "". 05 WINPUTSTRINGNOSIGN PIC X(19). 01 WSTRINGTOCONVERT PIC X(19). 01 WINTEGERPART PIC 9(18). 01 WDECIMALPART PIC X(18). 01 WDECIMALPARTASDECIMAL REDEFINES WDECIMALPART PIC V9(18). 01 WDECIMALPOINT PIC X VALUE ".". 01 WVEROUTPUTNUMBER PIC 9(8)V9(8). 01 FILLER REDEFINES WVEROUTPUTNUMBER. 05 WVEROUTPUTNUMBERINTEGER PIC 9(8). 05 WVEROUTPUTNUMBERDECIMAL PIC V9(8). 01 WFINALOUTPUTNUMBER PIC S9(8)V9(8). 01 FILLER PIC X. 88 WTRUNCATIONOCCURED VALUE "Y". 88 WTRUNCATIONNOTFOUND VALUE "N". 01 WFORMATTEDOUTPUTNUMBER PIC (7)9.9(8).
. IF WINPUTHASSIGN MOVE WINPUTSTRINGNOSIGN TO WSTRINGTOCONVERT ELSE MOVE WINPUTSTRING TO WSTRINGTOCONVERT ENDIF MOVE SPACE TO WDECIMALPART UNSTRING WSTRINGTOCONVERT DELIMITED BY WDECIMALPOINT OR SPACE INTO WINTEGERPART WDECIMALPART INSPECT WDECIMALPART REPLACING ALL SPACE BY ZERO DISPLAY "INPUT*" WINPUTSTRING "*" DISPLAY "*" WINTEGERPART "*" "*" WDECIMALPART "*" IF WINPUTHASSIGN DISPLAY "NEGATIVE" ENDIF COMPUTE WVEROUTPUTNUMBER = WINTEGERPART + WDECIMALPARTASDECIMAL SET WTRUNCATIONNOTFOUND TO TRUE IF WVEROUTPUTNUMBERINTEGER NOT EQUAL TO WINTEGERPART DISPLAY "INTEGER TRUNCATION" SET WTRUNCATIONOCCURED TO TRUE ENDIF IF WVEROUTPUTNUMBERDECIMAL NOT EQUAL TO WDECIMALPARTASDECIMAL DISPLAY "DECIMAL TRUNCATION" SET WTRUNCATIONOCCURED TO TRUE ENDIF IF WTRUNCATIONOCCURED MOVE ZERO TO WFINALOUTPUTNUMBER ELSE MOVE WVEROUTPUTNUMBER TO WFINALOUTPUTNUMBER ENDIF IF WINPUTHASSIGN SUBTRACT WFINALOUTPUTNUMBER FROM ZERO GIVING WFINALOUTPUTNUMBER ENDIF MOVE WFINALOUTPUTNUMBER TO WFORMATTEDOUTPUTNUMBER DISPLAY "*" WFORMATTEDOUTPUTNUMBER "*" DISPLAY " " .
Output is:
INPUT*1.1 * *000000000000000001**100000000000000000* NEGATIVE * 1.10000000* INPUT*1 * *000000000000000001**000000000000000000* * 1.00000000* INPUT*0 * *000000000000000000**000000000000000000* * 0.00000000* INPUT*0.1 * *000000000000000000**100000000000000000* * 0.10000000* INPUT*1 * *000000000000000001**000000000000000000* NEGATIVE * 1.00000000* INPUT*123456789 * *000000000123456789**000000000000000000* NEGATIVE INTEGER TRUNCATION * 0.00000000* INPUT*123456789 * *000000000123456789**000000000000000000* INTEGER TRUNCATION * 0.00000000* INPUT*123456789.987654321* *000000000123456789**987654321000000000* NEGATIVE INTEGER TRUNCATION DECIMAL TRUNCATION * 0.00000000* INPUT*123456789.987654321 * *000000000123456789**987654321000000000* INTEGER TRUNCATION DECIMAL TRUNCATION * 0.00000000* INPUT*0.1234567891 * *000000000000000000**123456789100000000* DECIMAL TRUNCATION * 0.00000000* INPUT*0.1234567891 * *000000000000000000**123456789100000000* NEGATIVE DECIMAL TRUNCATION * 0.00000000* INPUT*12345678 * *000000000012345678**000000000000000000* NEGATIVE *2345678.00000000* INPUT*12345678 * *000000000012345678**000000000000000000* * 2345678.00000000* INPUT*12345678.87654321 * *000000000012345678**876543210000000000* NEGATIVE *2345678.87654321* INPUT*12345678.87654321 * *000000000012345678**876543210000000000* * 2345678.87654321* INPUT*0.12345678 * *000000000000000000**123456780000000000* * 0.12345678* INPUT*0.12345678 * *000000000000000000**123456780000000000* NEGATIVE * 0.12345678*

Re: Converting strings to decimal
20121012T20:42:19ZThis is the accepted answer. This is the accepted answer.FYI,m NUMVAL does not do rounding or truncation, but the MOVE or IF statement that it is imbedded in might. If you store the result of NUMVAL into sufficiently large numbers you should be safe.
IE something like this:
77 BIGDEC PIC S9(25)V9(6) PACKEDDECIMAL.
. . .
COMPUTE BIGDEC = FUNCTION NUMVAL(input)
This seems so much simpler!
COBOL is the Language of the Future!
Tom 
Re: Converting strings to decimal
20121012T23:47:25ZThis is the accepted answer. This is the accepted answer. SystemAdmin
 20121012T20:42:19Z
FYI,m NUMVAL does not do rounding or truncation, but the MOVE or IF statement that it is imbedded in might. If you store the result of NUMVAL into sufficiently large numbers you should be safe.
IE something like this:
77 BIGDEC PIC S9(25)V9(6) PACKEDDECIMAL.
. . .
COMPUTE BIGDEC = FUNCTION NUMVAL(input)
This seems so much simpler!
COBOL is the Language of the Future!
Tom
The "something" would be this, I assumed, from the Language Reference:
"The returned value is a floatingpoint approximation of the numeric value represented by argument1. The precision of the returned value depends on the setting of the ARITH compiler option. For details, see Converting to number (NUMVAL, NUMVALC) in the Enterprise COBOL Programming Guide."
The Programming Guide:
"The arguments must not exceed 18 digits when you compile with the default option ARITH(COMPAT) (compatibility mode) nor 31 digits when you compile with ARITH(EXTEND) (extended mode), not including the editing symbols.
NUMVAL and NUMVALC return long (64bit) floatingpoint values in compatibility mode, and return extendedprecision (128bit) floatingpoint values in extended mode. A reference to either of these functions represents a reference to a numeric data item.
At most 15 decimal digits can be converted accurately to longprecision floating point (as described in the related reference below about conversions and precision). If the argument to NUMVAL or NUMVALC has more than 15 digits, it is recommended that you specify the ARITH(EXTEND) compiler option so that an extendedprecision function result that can accurately represent the value of the argument is returned."
Going to "S9(25)V9(6)" necessitates ARITH(EXTEND) anyway, so sufficient (in it being exact, as I read it) precision is gained.
The second possible element of "truncation" is of course the size of the "receiving" field, as you point out.
However, extending the size of the receiving field from NUMVAL then requires some further code to identify whether truncation will/has occurred when placed in its final destination field.
If the NUMVAL is in an IF and the value would be truncated in its final field, you have to rework that as well.
On top of that, changing compile options which affect codegeneration is nontrivial. For a start, ARITH may be "locked" to COMPAT. Even if not, I'd not care for a "mix" of modules, some with COMPAT and some with EXTEND, so to change one to EXTEND implies, to me, changing all within that particular system. Not necessarily something a manager will "buy" just for "simplicity".
So, I chose a 16digit final destination, but with a way to identify truncation either before or after the decimal point, without loosing any of the value from the input "string" and which wouldn't have any chance of loosing precision. With no need to even consider which suboption is used for ARITH.
The data shown was very simple. I felt it deserved a simple solution with no other "impacts". OK, yes, if the existing NUMVAL is in an IF, that would again need to be changed. Personally I feel an IF is not "simple" if it contains a FUNCTION anyway.
Whilst we are here, there is the notmentionedthistime problem with NUMVAL. Feed it something bad and the world collapses. With "string" numerics often coming from external sources, I always feel the need to "verify" (we used to call it "edit") them anyway. By the time you know it is OK for NUMVAL, you have the means to "do" the "numval" yourself :)
Maybe you have something in the near future for us on that? If you do, I guess you'll let us know at the time that you want to. I hope it isn't "use another FUNCTION to let us know it is OK to use NUMVAL which will do everything again that the first FUNCTION already did plus actually giving the result".
A simple solution to the simple data presented, which needs pay no heed to floatingpoint precision nor the setting of ARITH, is available. If to be needed in more than one place, stick it in a CALLed module. Like we've always done. Gosh. It's a bit like a "user function". Few parameters, and it can do all the possible truncation checking itself, and whatever else we may reasonably feel needed. It becomes a "simple" CALL without the NUMVAL "baggage" of precision and ARITH and with whatever results the designer of the module feels are needed, including identifying an invalid format without causing a disturbance. 
Re: Converting strings to decimal
20121013T14:05:32ZThis is the accepted answer. This is the accepted answer. BillWoodger
 20121012T23:47:25Z
NUMVAL and NUMVALC don't do rounding or truncation. I assumed there had been observation of "something" which was being interpreted, erroneously, as such.
The "something" would be this, I assumed, from the Language Reference:
"The returned value is a floatingpoint approximation of the numeric value represented by argument1. The precision of the returned value depends on the setting of the ARITH compiler option. For details, see Converting to number (NUMVAL, NUMVALC) in the Enterprise COBOL Programming Guide."
The Programming Guide:
"The arguments must not exceed 18 digits when you compile with the default option ARITH(COMPAT) (compatibility mode) nor 31 digits when you compile with ARITH(EXTEND) (extended mode), not including the editing symbols.
NUMVAL and NUMVALC return long (64bit) floatingpoint values in compatibility mode, and return extendedprecision (128bit) floatingpoint values in extended mode. A reference to either of these functions represents a reference to a numeric data item.
At most 15 decimal digits can be converted accurately to longprecision floating point (as described in the related reference below about conversions and precision). If the argument to NUMVAL or NUMVALC has more than 15 digits, it is recommended that you specify the ARITH(EXTEND) compiler option so that an extendedprecision function result that can accurately represent the value of the argument is returned."
Going to "S9(25)V9(6)" necessitates ARITH(EXTEND) anyway, so sufficient (in it being exact, as I read it) precision is gained.
The second possible element of "truncation" is of course the size of the "receiving" field, as you point out.
However, extending the size of the receiving field from NUMVAL then requires some further code to identify whether truncation will/has occurred when placed in its final destination field.
If the NUMVAL is in an IF and the value would be truncated in its final field, you have to rework that as well.
On top of that, changing compile options which affect codegeneration is nontrivial. For a start, ARITH may be "locked" to COMPAT. Even if not, I'd not care for a "mix" of modules, some with COMPAT and some with EXTEND, so to change one to EXTEND implies, to me, changing all within that particular system. Not necessarily something a manager will "buy" just for "simplicity".
So, I chose a 16digit final destination, but with a way to identify truncation either before or after the decimal point, without loosing any of the value from the input "string" and which wouldn't have any chance of loosing precision. With no need to even consider which suboption is used for ARITH.
The data shown was very simple. I felt it deserved a simple solution with no other "impacts". OK, yes, if the existing NUMVAL is in an IF, that would again need to be changed. Personally I feel an IF is not "simple" if it contains a FUNCTION anyway.
Whilst we are here, there is the notmentionedthistime problem with NUMVAL. Feed it something bad and the world collapses. With "string" numerics often coming from external sources, I always feel the need to "verify" (we used to call it "edit") them anyway. By the time you know it is OK for NUMVAL, you have the means to "do" the "numval" yourself :)
Maybe you have something in the near future for us on that? If you do, I guess you'll let us know at the time that you want to. I hope it isn't "use another FUNCTION to let us know it is OK to use NUMVAL which will do everything again that the first FUNCTION already did plus actually giving the result".
A simple solution to the simple data presented, which needs pay no heed to floatingpoint precision nor the setting of ARITH, is available. If to be needed in more than one place, stick it in a CALLed module. Like we've always done. Gosh. It's a bit like a "user function". Few parameters, and it can do all the possible truncation checking itself, and whatever else we may reasonably feel needed. It becomes a "simple" CALL without the NUMVAL "baggage" of precision and ARITH and with whatever results the designer of the module feels are needed, including identifying an invalid format without causing a disturbance. 
Re: Converting strings to decimal
20121013T14:21:00ZThis is the accepted answer. This is the accepted answer. lbjerges
 20121013T14:05:32Z
I would just like to comment on "Feed it something bad and the world collapses". You could actually write a condition handler to trap that type of event end do a softer termination.
But I don't want to do that for this. I want to process input numbers from an external source, reject but log those whose format are "bad" and process (or perhaps reject, depending) the rest. I want to not give NUMVAL bad data if it is going to fail. Simplest way to do that for now seems to be not to use NUMVAL unless you can rely on the quality of the data.
Catching an abend in LE each time I get bad data into NUMVAL is not what I want to do.