VO Compatible Arithmetic in X#

In the last days, we have been trying to (among other things) complete the VO compatibility aspect of X#, by implementing one final missing piece, behavior of numeric arithmetic that is completely compatible to Visual Objects.
The area where this becomes visible is when dealing with integral numbers (either variables or constants) with a size < 32 bits, so of the type BYTE, SHORT and WORD.
For example, consider this code:

LOCAL b1,b2 AS BYTE
LOCAL n AS INT
b1 := 250
b2 := 10
n := b1 + b2
? n

Currently this prints 260 in X#, as you may (or not!) expect. But in VO, the same code returns 4; the operation is performed between 2 BYTE vars and so is the result, which first overflows from the max value of 255 that can be stored in a byte and results to that value of 4 (260 mod 256), which is then in turn assigned to the INT var.

The current behavior of X# is compatible to c# and we intend to keep it as the default one, but plan (or had planned!) to implement the VO compatible behavior optionally, when the compiler option /vo11 (Compatible arithmetic conversions) is enabled. The problem is, that the more we research the behavior of VO, in order to fully implement it, the more weird and almost random it appears to be. For example, let's change the above example, to use constant byte values instead of variables in the expression:

LOCAL n AS INT
n := BYTE(250) + BYTE(10)
? n

Well, now also VO returns 260 instead of 4! It gets weirder; let's store the result in a USUAL this time, instead of an INT, without changing anything else:

LOCAL n AS USUAL
u := BYTE(250) + BYTE(10)
? u

And now the result in VO is 4 again!!!

It gets even better. As we saw before, the following:

LOCAL n AS INT
n := BYTE(250) + BYTE(10)
? n

Gives us a value of 260. Let's add one more operand now, to increase the result by 1:

LOCAL n AS INT
n := BYTE(250) + BYTE(10) + BYTE(1)
? n

Clearly, this will return 261, right? Well, no, the result that VO gives now is 5!!!

The inconsistency does not have to do only with constants; let's try using variables again, but now in multiplication, not addition of bytes:

LOCAL b1,b2 AS BYTE
LOCAL n AS INT
LOCAL u AS USUAL
b1 := 250
b2 := 10
n := b1 * b2
? n // 2500
u := b1 * b2
? u // 196

So, when storing the result to an INT variable, the expression returns 2500. But when it is stored to a USUAL, it returns 196 (2500 mod 256) in VO!

And so far we have only tested behavior of expressions using BYTE vars only. When we add to the mix different numeric types (WORD, SHORT, INT and DWORD), the results become more and more inconsistent. It is certainly not the first time we are facing problems like that, when we attempt to emulate the behavior of VO on several aspects in X#, but in most cases, after some research, we are able to deduce some rules (which still may be very inconsistent at times) which fully describe the behavior of VO and then adjust the compiler and/or runtime in X# to (optionally) match it. But, in this case, there's really no set of rules that describes this behavior, for the most part it is almost random practically. We've been ourselves professional VO developers for many years, and saying the above does not feel very nice, but it's what we unfortunately see in this area.

The question is, what can we do about it? Clearly, there's no way to fully emulate this mess in X#, because, even if we wanted to, we just can't cover all the very different and conflicting combinations of results that VO gives, so we've now started to think about completely abandoning this part of VO compatibility and just continue using the universal standard .Net rules for numeric arithmetic. Another option would be to try to emulate some of this only, but we are not sure which and how far we could or should try to go with it.

We would love to hear your (VO developers') opinion and/or thoughts though. What would you suggest/prefer us to do on this matter?

PS. Just to be completely clear, the above applies only to the VO compatibility aspect of X#, and still, that would only be optional behavior. So please do not be concerned about us modifying the behavior in Core, FoxPro or other dialects of X#!


4 comments

  • When I think on our code, I would say:
    Byte and Shortint/word are only used, if WinAPI-Methods have to be called, that need these parameters. Example: Crystal Report functions have a short int "job number", that is returned once and then passed to every further call without changes. This usage should never use a desired overflow as in the case of 255+1 equal to 0 for byte. So 250+10 can give 260, if assigned to an int.

    More critical is the mixing of word and shortint or dword and int. We have often the case of conversions of int to double and back to int. Or we subtract a dword from an integer. In these cases there shouldn't be any overflow errors. There are rare cases, where the maximum value of int is exceeded and here the calculations are made fully in dword and not mixed int/dword.
  • Arne, agreed, if you do not make calculations with the BYTE/WORD/SHORT values, then you should be always fine. Regarding INT and DWORD, we should be fully compatible with VO, at least when /vo11 is enabled. If you notice any case that isn't, please report it as a bug. I really hope we do not find weird behavior like that in VO also with INT/DWORD values, but so far we haven't.
  • If the real nightmare was not outside right going out of our houses, we could say, that this weird behavior would be a nightmare. :cry: With all the examples you have listed, you could start a competition among all the long time VO programmers and no one would succeed getting 100% correct!
  • Fortunately where I live there's no problem at all outside (at least not yet!), so for me the nightmare researching this behavior was real! :) And for Robert of course, who attempted to implement some of this behavior in the X# compiler... Btw, I could list even more samples, but thought it's enough already :)