Don't get bitten by automatic converions

I knew this, but still got bitten anyway. Great way to start the year :slight_smile:

For this code

Dim FirstDoubleVar As Variant
Dim SecondDoubleVar As Variant
FirstDoubleVar = 8.0
SecondDoubleVar = 8.2

If ((FirstDoubleVar \ 1) = FirstDoubleVar) Then
  System.DebugLog "FirstDoubleVar Is Whole"
else
  System.DebugLog "FirstDoubleVar Is Not Whole"
End If

If ((SecondDoubleVar \ 1) = SecondDoubleVar) Then
  System.DebugLog "SecondDoubleVar Is Whole"
else
  System.DebugLog "SecondDoubleVar Is Not Whole"
End If

Dim FirstDouble As Double
Dim SecondDouble As Double
FirstDouble = 8.0
SecondDouble = 8.2

If ((FirstDouble \ 1) = FirstDouble) Then
  System.DebugLog "FirstDouble Is Whole"
else
  System.DebugLog "FirstDouble Is Not Whole"
End If

If ((SecondDouble \ 1) = SecondDouble) Then
  System.DebugLog "SecondDouble Is Whole"
else
  System.DebugLog "SecondDouble Is Not Whole"
End If

If ((FirstDoubleVar.DoubleValue \ 1) = FirstDoubleVar.DoubleValue) Then
  System.DebugLog "FirstDoubleVar.DoubleValue Is Whole"
else
  System.DebugLog "FirstDoubleVar.DoubleValue Is Not Whole"
End If

If ((SecondDoubleVar.DoubleValue \ 1) = SecondDoubleVar.DoubleValue) Then
  System.DebugLog "SecondDoubleVar.DoubleValue Is Whole"
else
  System.DebugLog "SecondDoubleVar.DoubleValue Is Not Whole"
End If

The result is
FirstDoubleVar Is Whole
SecondDoubleVar Is Whole
FirstDouble Is Whole
SecondDouble Is Not Whole
FirstDoubleVar.DoubleValue Is Whole
SecondDoubleVar.DoubleValue Is Not Whole

So even though SecondDoubleVar is 8.2, the code says it is whole.
as far as I can tell the following line
If ((SecondDoubleVar \ 1) = SecondDoubleVar) gets interpreted the following way
SecondDoubleVar \ 1 is an integer
Therefore we compare by converting the second part (SecondDoubleVar) to integer
and then we compare
so we compare 8 (SecondDoubleVar \ 1) with 8 (the converted integer value of SecondDoubleVar)
Since 8 is 8, we can conclude that 8.2 is whole

Of course, if you force the variants to Doubles (SecondDoubleVar.DoubleValue) before you make the comparison, you don’t have this problem.

:sweat_smile:

There’s a huge bug with Variants here.

“Integer = Double?” can’t be normalized to “Integer = Integer?” (losing meaning) but “Double = Double?” (promote to the largest container type keeping the meaning of the value).

Someone should open an Issue for:

Const testValue = 8.2

Var testVar As Variant = testValue
var testDbl As Double = testValue

if testVar \ 1 = testVar Then
  
  MessageBox "Variant Error"
  
Else
  
  MessageBox "Variant works ok"
  
End

if testDbl \ 1 = testDbl Then
  
  MessageBox "Double Error"
  
Else
  
  MessageBox "Double works ok"
  
End

Quit

That’s why in the past articles like “Variants are Evil” were created. This is a design limitation.

As Integer Division result is an integer then as you are not specifying that you want a Double from the Variant, then: “Variants used in an expression convert themselves to the type of the other operand” as mentioned in the documentation here.

Variants are evil. They can produce very subtle errors with number precision that are hard to catch.

4 Likes

That’s a bad design, but faster than the proper one (RTTI evaluation of both parts and promotion to proper types), but open to insane errors.

VB.Net does it correctly

1 Like

That’s why I posted this here, as a warning.
Like I said converting it to what you want (DoubleValue) will give the correct result.
That is also what the manual says:
“If there is any ambiguity concerning the type conversion of a Variant, you should use one of the properties of the Variant class to force the compiler to convert the Variant to the desired type”
But it can indeed cause unexpected issues if you don’t do the convertion, as seen in my example.

1 Like