Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Comparisons Not Correct

Status
Not open for further replies.

stanlyn

Programmer
Sep 3, 2003
945
US
Hi,

Over the years I've run into this issue when comparing values and it is happening again. So, here I am asking you guys the big why?

Code:
lcUserID = Inputbox('Enter UserID String', 'License Server UserID', lcDecUserStr, 30000)
	
If Alltrim(lcUserID) != Alltrim(lcDecUserStr)
	Replace Setup.ls_conn_user_string With goCrypt.EncryptStringENC(lcUserID)

The value for alltrim(lcUserID) is "sa", and the value for Alltrim(lcDecUserStr) is "" (empty). I've also replaced != with <> with same incorrect results.

This is clearly not equal to each other no matter how we see it. Setting exact=on makes it show as .T. in the debugger. However, we can clearly see they are NOT equal, so why does the debugger see it differently?

Can anyone shed some light on this, as I've had this unexpected behavior over many years now.

See screenshot of debugger...

Thanks, Stanley

 
Hi,

Either you choose SET EXACT ON, either you replace your comparison with :

If !(Alltrim(lcUserID) == Alltrim(lcDecUserStr))
or
If NOT Alltrim(lcUserID) == Alltrim(lcDecUserStr)

P.S. I believe the braces are superfluous, but the expression is clearer with them.


Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
= is usbject to exact on/off, != and <> too, but if you SET EXACT ON, the comparisons will make sa differ from the empty string.
>Setting exact=on makes it show as .T. in the debugger
Are you saying the debugger shows .T. for Alltrim(lcUserID)<>Alltrim(lcDecUserStr) with EXACT ON And lcUserID="sa" and lcDecUserStr=""?
That's correct isn't it?

With EXACT OFF the comparsons are for example char field friendly, as char fields values are always padded with spaces to the field width, now if you'd have a for or where clause of table.cName="Stan" that would be wrong for table.cName"Stan ", if string comparisons would compare exactly and the char field has that length. So it's done to simplify code. How many users do you think would get why storing their name into a char field and comparing it to their name again will show .F.? The inexact setting is there to avoid that problem. Sinve VFP9 we also have ANSI on/off influencing the comparisons for SQL separately from other comparisons.

If you need exact comparisons there is the == operator and while there is no !== operator, you can always put the ! or NOT in front and use (NOT a==b), as Vilhelm-Ion shows, that's the solution for that problem.

Alltrim is a good as preparation, if you want to compare strings excluding whitespace, but it's not enough, you have to use == or exact on, otherwise a solution would rather need padding both sides to same length than trimming it, as trimming leads to differing length which is making = and <> compare only partially.

You always have the have same thoughts for = and <>, as <> simply is the negated result of the = operator. Especially comparisons of any string with the empty string x="" is always .T. with EXACT OFF and therefore x<>"" is always .F. for x being any string (including the empty string, when it's really true, of course).

The result differs for swapped values, ie ""="sa" is .F. and ""<>"sa" is .T., but the simple solution is using expr1==expr2 and NOT expr1==expr2.

Bye, Olaf.




 
Stanley,

Are you sure about the setting of SET EXACT? If EXACT is OFF (which is the default), that would explain what you are seeing.

To explain, let's strip away all the stuff about the input box and the REPLACE command. The core of the matter is that you are comparing "" with "sa". If you do this:

[tt]? "" = "sa"[/tt]

you will always get the expected result, which is .F.. But if you do this:

[tt]? "sa" = ""[/tt]

you will get .F. if, and only if, you have EXACT ON.

The reason is that VFP starts the comparison on the first character, then continues along the string until until the end of the string on the right-hand side. So the position of the strings in respect of the equals sign is significant.

Put another way, it's possible for a to equal b, even though b doesn't equal a. I now it's weird but that's the way xBase has always worked. The best way to avoid any problems is to use the double equals sign (a == b, b == a).

Finally, keep in mind that SET EXACT is scopted to the data session. You might think you have set it in a certain way, but that setting might no longer apply if you are in a different data session.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Good point about the scope of SET EXACT.

I'd like to add a simple way to remember what the operator does in EXACT OFF mode, it's doing a "begins with" comparison, left hand expression begins with right hand expression. Every string begins with the empty string, so everything="" and nothing<>"". It's no solution to simply swap the comparison, that's only solving the special case of comparisons with the empty string. Alltrim(lcDecUserStr) will not stay empty.

Bye, Olaf.
 
MikeLewis said:
The reason is that VFP starts the comparison on the first character, then continues along the string until until the end of the string on the right-hand side.

Many years ago I was teaching a Foxpro 2.6 class (half the class using FP/Mac and the other class using FP/Win in OS/2). When I explained this rule the director of IT sat bolt upright and blurted out "THAT EXPLAINS A BUG I'VE BEEN CHASING FOR YEARS!"

As you say, this has always been Xbase behavior. It's just not particularly well known Xbase behavior. [smile]
 
I also remember how upset I was when first discovering this. It may well have been a CASE statement with two cases x="something" and another x="somethingelse". The "somethingelse" case never occurred...

Bye, Olaf.
 
>> blurted out "THAT EXPLAINS A BUG I'VE BEEN CHASING FOR YEARS!"
Me too, and yesterday I decided to attack it and find out why...


>> It's no solution to simply swap the comparison
In my case here, I really expected that swapping would change the result, but it did not. Nothing I tried worked except for setting Exact=on.


>> this has always been Xbase behavior.
Good to know as I can now avoid wasting time swearing against the fox...


>> Are you saying the debugger shows .T. for Alltrim(lcUserID)<>Alltrim(lcDecUserStr) with EXACT ON And lcUserID="sa" and lcDecUserStr=""?
The actual values are: Alltrim(lcUserID) equals "sa" and Alltrim(lcDecUserStr) equals "" (empty), therefore the fox is wrong if it thinks the comparison is .F. We can clearly see the two values are not equal to each other (swapping or not) and when asking fox whether the two values are not equal (<> or !=) fox says they are not, but clearly they are. I've wasted a lot of time in the past on this sort of thing. However, I can accept as I don't have a choice, its flawed behavior and code with that in mind.

With exact=off, Alltrim(lcUserID)<>Alltrim(lcDecUserStr) equals .F. (see previous screenshot)
With exact=on, Alltrim(lcUserID)<>Alltrim(lcDecUserStr) equals .T.

Someone will say it isn't flawed, and I beg to differ as anytime that "" equals "a" or "a" equal "", then someone, (probably me) has lost their mind...

Anyway, thanks for this discussion as I have benefited and hopefully others have too,

Thanks,
Stanley
 
>> I also remember how upset I was when first discovering this.

Thanks Olaf, I don't feel so bad now, except for the all the time I've wasted on this, now that I know, thanks to this discussion... I was always able to get around the issue by spending countless time doing stuff like multiple if statement testing along the way...

I remembering "pushing back from my desk" and thinking how in the ??? can "a" equal "" and vice versa... Makes absolutely no sense to me!


Thanks, Stanley
 
Well, the way it is defined is surely not the mathematical definition, but maths also doesn't have == as "exactly equal" operator. Other languages have such operators, too. Eg PHP and javascript both have = for assignments, == for simple comparisons and === for identical comparisons. I'll not go into the details.

The way it is in VFP with EXACT OFF "sa"="" is .T. , but ""="sa" is .F., therefore "sa"<>"" is .F. and ""<>"sa" is .T.

It's true that swapping isn't helping for the general case., but it would have helped in this special case and if it didn't, you didn't really swapped. You'll always need to put the shorter string on the left hand side and determining the shorter string will also cost time, so you better befriend yourself with the == operator, and thank god VFP is simpler than other languages despite that strange definition of equality, as other languages are even more verbose about this.

Bye, Olaf.
 
I also remember how upset I was when first discovering this.

Yeah, me too. When I found out about it, I promised myself that I would always put SET EXACT ON at the top of every PRG I ever wrote. Of course, that was before I had to worry about data sessions or encapsulation of objects or even writing generic code.

It's best just to accept it. And we shouldn't blame the fox. If there is anyone to be admonished, it is Mr Ratliffe.

Mike





__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Hi Mike,

Code:
? "sa" = ""
you will get .F. if, and only if, you have EXACT ON.
The reason is that VFP starts the comparison on the first character, then continues along the string 
until until the end of the string on the right-hand side. So the position of the strings in respect 
of the equals sign is significant.

Would you break the comparison down char-by-char on this to explain, as I'm not getting it...

Thanks,
Stanley
 
Stanley,

Maybe I didn't explain it very well. What I meant was that the comparison stops at the end of the right-hand string when EXACT is OFF. By setting it ON, you avoid that behaviour and the comparison always works as expected. I can see that my previous explanation might have been confusing. Sorry.

Mike



__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
The comparison isn't so much a measurement of equality as it is a search for inequality.

Given this:

Code:
?"aa"=""

Xbase compares the first character on the left to the first character on the right, then the second character on the left to the second character on the right, continuing until the string on the right ends.

In this case, there are no characters on the right so there is no comparison. With nothing to compare, no inequality is found. Convoluted as all get out, ain't it?

But it gets us useful behaviors too:

Code:
SEEK "Anderson"

With EXACT off, this will find Anderson, Andersonville, etc. -- using the same loose "search for inequality".
 
Thanks Dan,

It is a little comforting to see that others have struggled with this too, as I was blaming fox and me. I've pushed back many times thinking "there is no way that 'aa' can equal '' (nothing)", and I'm glad for this discussion that sheds light on fox''s way of thinking.

Thanks again,
Stanley
 
A couple of additional points because I don't see that anyone else has said them.

First, the reason for this behavior is that dBase was originally viewed as a power-user tool. When you're typing things into the Command Window (well, back then, the dot prompt), this is handy behavior.

Second, in Xbase code, the rule is that the comparison continues to the end of the right-hand string. In SQL code (that is, using VFP's SQL commands), it doesn't matter which side is shorter; the comparison ends at the end of the shorter string, and if the strings match to that point, then they match. However, SQL commands aren't controlled by SET EXACT; the corresponding command is SET ANSI.

Tamar
 
Stanlyn said:
It is a little comforting to see that others have struggled with this too

Oh, it's not just this. xBase is frequently easier to use than it is to learn. (And many things are easier to do than they are to explain.)

I was once in a board meeting for our local user group when the President of the group came in FUMING. He was ENRAGED. "They changed the way SET RELATION works in the latest beta!" he fumed. We were all on that beta too, so we just blinked and asked for more information.

He went on "Now they require the target to have an order set!"

We all calmly replied "that's how it has always worked". It turns out that for seven years he'd just been lucky.
 
Hi,
Code:
Second, in Xbase code, the rule is that the comparison continues to the 
end of the right-hand string.
In the case I posted, how can aa that contains a value be equal to "" (nothing) and
how can "" (nothing) ever be equal to aa which contains a value using this Xbase rule?


For me, I wasn't very lucky, as I ran into this issue every once in a while, and wasted
a lot of time creating work arounds until I got what I was expecting. All because, in
my mind aa that equals "Stan" could never equal "" nothing, no matter how we set exact
or swap sides.


Code:
The comparison isn't so much a measurement of equality as it is a search for inequality.
This helps a lot as I've never heard that perspective before...

Code:
In this case, there are no characters on the right so there is no comparison. 
With nothing to compare, no inequality is found. Convoluted as all get out, ain't it?
So, is it fair to say that the fact that one side had a value of nothing, this created
the unexpected behavior? Whereas, if it contained something (other that nothing) the
opposite outcome would be true. So is this really the testing against nothing issue here?

Stanley
 
stanlyn said:
So, is it fair to say that the fact that one side had a value of nothing, this created
the unexpected behavior? Whereas, if it contained something (other that nothing) the
opposite outcome would be true. So is this really the testing against nothing issue here?

No, it's not just empty strings. Try this:

Code:
?"stanlyn"="s"

The comparison ends after the only character on the right and no inequality is found. It returns .t.
 
And one last thought about this.

A lot of the time we're comparing variables against field values, so this comparison:

Code:
? "stanlyn" = namefield

Is actually a comparison against a right-padded value the length of the field. If the field is c(20), the actual comparison is:

Code:
? "stanlyn" = Padr("s",20)

The comparison will find inequality when it hits the first trailing space. That's why it doesn't manifest all that often.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top