<body><script type="text/javascript"> function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener('load', function(){ object[attribute] = val; }, false); } else { window.attachEvent('onload', function(){ object[attribute] = val; }); } } </script> <div id="navbar-iframe-container"></div> <script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script> <script type="text/javascript"> gapi.load("gapi.iframes:gapi.iframes.style.bubble", function() { if (gapi.iframes && gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: 'https://www.blogger.com/navbar.g?targetBlogID\x3d34883048\x26blogName\x3dYour+Friendly+ABAPer\x26publishMode\x3dPUBLISH_MODE_BLOGSPOT\x26navbarType\x3dBLUE\x26layoutType\x3dCLASSIC\x26searchRoot\x3dhttp://friendlyabaper.blogspot.com/search\x26blogLocale\x3den_US\x26v\x3d2\x26homepageUrl\x3dhttp://friendlyabaper.blogspot.com/\x26vt\x3d-4712760677496349421', where: document.getElementById("navbar-iframe-container"), id: "navbar-iframe" }); } }); </script>

Fun with numbers

It seems that ABAP is one of the few languages that does not have an operator to validate whether the field is numeric or not. In the system where most of the key fields (VBELN, MATNR, etc.) are CHAR but usually contain only numbers an IS NUMERIC operator would come in handy, don’t you think?

Oh well, as Mr. Zorg used to say: "You want something done, do it yourself!". And so I started this quest with a simple task to find a way to check whether the field is a number. I’m not going to lie to you – the idea to define a constant with numbers only and to use IF ... CO... was stolen from one of the SAP programs. Here is my first test program:

CONSTANTS: numbers(10) VALUE '1234567890'.
DATA: test(10).

test = '123ABC'.
PERFORM test_check USING test.

test = '123'.
PERFORM test_check USING test.

FORM test_check USING p_test.
IF p_test CO numbers.
WRITE: / p_test , ' contains only numbers'.
ELSE.
WRITE: / p_test , ' contains alpha characters'.
ENDIF.
ENDFORM. " test_check
But the result surprised me:

123ABC contains alpha characters
123 contains alpha characters
What?! Since when 123 is not a number?! Well, obviously, sometimes the owls are not what they seem. Here is what the documentation says:

CO (Contains Only):

c1 contains only characters from the string c2.
If c1 or c2 is of type C, the comparison takes into account the full length of the field, including blanks at the end.
Doh! Damn SAP with their blanks... OK, I can work around this. Not sure if there is a better way to do this but I found my own method to pad a number with leading zeroes by using SHIFT and TRANSLATE operators. Here is my test number two (I changed only the test_check routine:

FORM test_check USING VALUE(p_test).

SHIFT p_test RIGHT DELETING TRAILING ' '.
TRANSLATE p_test USING ' 0'.

IF p_test CO numbers.
WRITE: / test , ' contains only numbers'.
ELSE.
WRITE: / test , ' contains alpha characters'.
ENDIF.
ENDFORM. " test_check
The result:

123ABC contains alpha characters
123 contains only numbers
Tadah! Note that if you don’t use FORM ... USING VALUE... then the variable test will be converted to ‘0000000123’. (Boy, I feel so smart right now. :) )

OK. But this piece of magic has actually very limited application. What if the field contains characters like ‘+’, ‘,’ or ‘.’, which can also be a part of the number? Coincidentally, on one of the SAP forums someone has posted a question how to convert a string (for example '107,400.99') into an integer. Since I was already on this numeric quest, I continued in this new direction. Here is what I came up with:

DATA: input_string TYPE string,
output_integer TYPE i.

input_string = '+107,400.99'.

CATCH SYSTEM-EXCEPTIONS
arithmetic_errors = 1
conversion_errors = 2.
TRANSLATE input_string USING ', '.
CONDENSE input_string NO-GAPS.
output_integer = input_string.
ENDCATCH.

IF sy-subrc = 0.
WRITE: output_integer.
ELSE.
MESSAGE 'Not a number' TYPE 'E'.
ENDIF.
The result: 107,401 (since its type I the decimals have been rounded). This piece of code also works when input_string is type CHAR and output can actually be any numeric type. It works with NUMC, P (with or without DECIMALS) and currency types equally well. Also the input string can have plus and minus sign and it can be upfront or at the end of the number – it will still work. The standard ABAP type conversion will take care of converting decimals and the sign, so we only have to remove the thousand separator (‘,’ in this case). CATCH clause will catch an exception if the string contains any other characters (e.g. letters).

Of course, there are also some related functional modules available:
MOVE_CHAR_TO_NUM – This is a good all-purpose FM (works with CHAR but not with STRING though). Its major advantage is that the thousands separator is not limited to a comma, like in my example above.
HRCM_STRING_TO_AMOUNT_CONVERT – this FM can be used for the conversion of amounts since it takes the currency into account. For the simple string to number conversion it is a bit too bulky, in my opinion.
CATS_NUMERIC_INPUT_CHECK – this one has a very limited application IMHO. It does not convert character fields to numeric fields. Basically all it does it checks if the field is numeric, removes the thousands separator and, if there is a negative sign, moves it to the end. It could not handle the number '+107,400.99' and threw a "not numeric" exception.

posted by Your Friendly ABAPer @ 21:01,
Direct link to this post

6 Comments:

At 26/2/09 17:33, Anonymous Anonymous said...

You are a functional consultant who fancies himself as a developer, aren't you? ;-)

 
At 14/8/09 14:54, Anonymous Your Friendly ABAPer said...

No, actually it's the other way around. :)

 
At 20/10/09 04:05, Blogger Pete said...

Hi,

another way would be to add SPACE in your number constant:

CONSTANTS: numbers(11) VALUE '1234567890 '.

Regards, Peter

 
At 26/10/09 21:16, Anonymous Your Friendly ABAPer said...

Yes, it's an option. Although, come to think of it, there is kind of a loophole - if 'space' is included in the constant, then input like '1 2' (1 space 2) would be considered OK and numeric. With my TRANSLATE command this would be translated into '102', which is also not good. So "use at your own risk" and consider other options, depending on your particular requirements. Cheers!

 
At 24/12/14 01:42, Anonymous Anonymous said...

nice blog!

is there any simple way to check such case? eg. the input is 99,9,,,99 or 9999,9,99,9. It should not be recognized as num. But to check this it will cost much effort to coding.

 
At 18/1/17 15:42, Blogger Marius Piedallu van Wyk said...

Also look at the UNPACK instruction... I found this useful fo doing zero padding automatically when working with numbers.

 

Post a Comment

Links to this post:

Create a Link

<< Home