Subject: Re: MOVEL in freeform From: Scott Klement Date: Tue, 05 Aug 2008 12:06:34 -0500 List-archive: List-help: List-id: RPG programming on the AS400 / iSeries List-post: List-subscribe: , List-unsubscribe: ,

Hi Raul,

The problem with %int or %dec is the sign. 0M zoned is -4, int or dec report it as error.

I must be doing a very poor job of explaining myself, as this is now the third time I'm explaining it in this thread, and frankly, it's not a very complicated concept.

Pretend variables don't exist for a moment. Variables don't matter. It's bytes in memory that matter.

Alan has this in the bytes in memory: x'F0F0F0F0F0F0F0F0D4'

What does that look like to you? If you think about it, it's zoned decimal. The bytes exactly follow the rules of zoned decimal. Despite the fact that it's in an alphanumeric field, it's truly a collection of bytes that's intended to be a zoned decimal number.

Can you use %dec() or %int() to convert it to a numeric variable reliably? Of course not!! The data isn't in text format, it's in zoned decimal format! Sure, if you desire the output in integer format, you could use %int() to make it an integer field -- but you have to tell RPG that the data is ZONED DECIMAL not ALPHANUMERIC if you want that to function properly. In other words, feeding the alphanumeric field into %int() won't work. (And I can't see any value to %dec() in this instance, since the data is *already* in decimal format).

So what do you want to do? Is the desired goal to run these bytes through a routine that converts from x'F0F0F0F0F0F0F0F0D4' to x'FFFFFFFD'? That's a large change, isn't it? It changes the field from 9 bytes to 4 bytes. It changes the values of the bytes dramatically. And, why would you want to do that? If the end result of an 'integer' field is what's desired, sure... you can do that, but I'd suggest coding it in RPG rather than C. i.e. first use a DS to view that same memory as zoned, then use %int() to make it an integer. But, I don't think the desired result is the integer (x'FFFFFFFD') format.

The data is already in a valid zoned decimal format. All you really want to do is view that same memory as a zoned field instead of viewing it as an integer field.

You don't need a complex API to convert the data from one format to another because it's already in a useful format. Just view the memory as zoned:

D char ds
D zoned 9s 0

When you read the field in, read it into the field named 'char', above. Without any further work being done by the CPU, you can now read the 'zoned' field, and it's in zoned decimal format. The memory is /already/ zoned decimal, and now you're viewing it as such instead of viewing it as alphanumeric.

Or, if it's easier to call a routine, here's a 5-second routine that simply copies the memory from any arbitrary point in memory to a zoned decimal field:

p MakeZoned b
d MakeZoned pi 9s 0
d Input * value
D Zoned s 9s 0 based(input)
c return zoned
P E

That routine will run faster than the QXXZTOI routine because it's a much simpler routine -- it just copies the memory, it doesn't have to do the conversion.

Honestly, the routine you're calling, QXXZTOI is intended to work the same way as RPG's %dec() BIF, except that it has fewer features than %dec(). QXXZTOI can only accept input in zoned format, whereas %dec() can accept input in many different formats.

When calling QXXZTOI from RPG, you really SHOULD prototype it like this:

D cvtZonedToInt pr 10i 0 extproc('QXXZTOI')
d cvtZone 9s 0 const
d cvtDigits 10i 0 value
d cvtFraction 10i 0 value

The only reason the C prototype doesn't use zoned decimal for the first parameter is because C doesn't support zoned decimal as a data type. Thus, if you're going to have a zoned decimal field in C, you have to store the raw bytes in a character field. RPG doesn't have that limitation, it supports zoned natively.

The purpose of the API isn't to convert from character to zoned, it's to convert from zoned to integer. Since your data isn't in character/text format to begin with, it's actually in zoned format to begin with, it just HAPPENS to work for you. You've coded the parameters like this instead:

D cvtZonedToInt pr 10i 0 extproc('QXXZTOI')
d cvtZone * value
d cvtDigits 10i 0 value
d cvtFraction 10i 0 value

By prototyping it this way, you've basically told it to do exactly what the data structure did. You've told it to view the area of memory addressed by the pointer as zoned decimal -- and therefore, it's viewing the character field as zoned instead of character. But, this API has the extra overhead of ALSO converting it to integer, which isn't really needed in this instance. Therefore, the API isn't really the right tool.

To put it in RPG terms (though, I'm not sure why, since you all seem to really want to do this in C code) the QXXZTOI API with the pointer parameter on the prototype does this:

D char ds
D zoned 9s 0
D int s 10i 0
/free
int = %int(zoned);

That's all the API does. All I'm saying is that the last step of converting to integer is not necessary. You really want a decimal field, not an integer. So this is what you really want to do:

D char ds
D zoned 9s 0
/free
// use "zoned"

I think that's all the OP needs is to get it into zoned.

However, in my original message, I suggested that if for some reason the OP wants the extra funcitonality of having it converted to another data type such as integer, date, or packed, he can still call the %int, %dec, or %date BIF. That doesn't mean that you can omit the data structure part and only use %dec(), Raul. Just that if these other formats are needed, they ARE available after you've already converted it to zoned.

I'd rather see the code written in pure RPG rather than calling a C function. Just my opinion. I work in C frequently, and I think everyone knows that I don't hesitate to call C routines when I need them -- but I just don't see how one is needed in this case.