Link to home
Start Free TrialLog in
Avatar of pwebonline
pwebonline

asked on

Bug in vb6 comparing int and double

I am going nuts because the following sentence: Int(dCantidadRegistro) = dCantidadRegistro returns false when debugging dCantidadRegistro = 1 so I don't understand anything anymore. The app runs the following code a million times over and over in a thouthand files and each time it fails in a different file so I guess it's some kind of VB bug. I am using VB 6.0 SP6

Dim iindice As Integer
Dim iFreeFile As Integer
Dim dCantidadRegistro As Double
Dim RegistroDJVE2 As RegEntradaDJVE2

Dim LongArchivo As Integer
Dim LongConfArchivo As Integer

iFreeFile = FreeFile

        Open Trim(sArchivo) For Random Lock Read Write As #iFreeFile Len = (Len(RegistroDJVE2))

        LongArchivo = FileLen(sArchivo)
        LongConfArchivo = Len(RegistroDJVE2)
        dCantidadRegistro = FileLen(sArchivo) / Len(RegistroDJVE2)
        'dCantidadRegistro = LongArchivo / LongConfArchivo
       
        'Verify file is not empty
        If (dCantidadRegistro > 0) Then
           
            If (Int(dCantidadRegistro) = dCantidadRegistro) Then
               'Everything is ok so process file
               Call Processfile(RegistroDJVE2, sArchivo, iindice)
            Else
                'Something is wrong with either the file or the structure                
                ShowError "Error de estructura de archivo " & Trim(ArchivoExtension) & " (REG" & LongConfArchivo & "-FILE" & LongArchivo & ")"
           
            End If
   
        Else
           
            'Incrementa variable global de conteo de archivos en blanco
            BlankFiles = BlankFiles + 1
   
        End If

'''''''''''''''''''''''
' Module
'''''''''''''''''''''''
    Public Type RegEntradaDJVE2
        TipoMovimiento As String * 1
        NUMDJVE As String * 16
        Tripli As String * 8
        CANTI As String * 17
        CUMPLIDO As String * 1
        FECTXT As String * 8
        HORTXT As String * 6
        Cliente As String * 5
        CUIT As String * 11
        REFCLI As String * 20
        FECCUM As String * 8
        ADUTRA As String * 3
        NUMDESP As String * 7
        LETDESP As String * 1
        TIPDESP As String * 4
        sFiller As String * 157
        FinLinea As String * 2
    End Type
Avatar of Jaime Olivares
Jaime Olivares
Flag of Peru image

you can use mod operator:

if (FileLen(sArchivo) mod Len(RegistroDJVE2) = 0) then
   'etcetera

Avatar of pwebonline
pwebonline

ASKER

Does it make a difference?... I mean, is there any chance mod will fail too?
Mod fails too. I don't understand the inmed mode shows:
?FileLen(sArchivo)
 275
?Len(RegistroDJVE2)
 275

What is wrong?
Mod is not good....
?FileLen(sArchivo) mod 1.2
 0
>Mod fails too. I don't understand the inmed mode shows:
>?FileLen(sArchivo)
> 275
>?Len(RegistroDJVE2)
> 275

What is the problem with this?
275 mod 275 must be 0 (meaning exact division). Or maybe you have a different result?

>Mod is not good....
>?FileLen(sArchivo) mod 1.2
> 0
Mod is good for integer divisions, this is not a good example
I know but the comparition is supposed to verify if the file right or not since the file is written by another application which I don't control. Suppose the file is wrong:
?100 mod 10
 0

?10 mod 1
 0

?275 mod 1
 0
etcetera
mod 1 is the unique exception, you can manage it easily with an If sentence
maybe:

If FileLen(sArchivo) < Len(RegistroDJVE2)

BTW. You don't need to use parenthesis in an If sentence in Visual Basic.
hmm..Interesting. Are you saying that,
say
dCantidadRegistro = 1
Debug.Print Int(dCantidadRegistro) = dCantidadRegistro
returns false????

then thats weird.

you might want to try this approach. look for a decimal place. If its perfectly divisible, double does not have 0
If Instr(dCantidadRegistro,".") = 0 then
----- OK
I know it's weird....
?dCantidadRegistro
 1
?Int(dCantidadRegistro) = dCantidadRegistro
False
?Int(dCantidadRegistro)
 0
?Instr(dCantidadRegistro,".")
 0

I don't use mod because it's not right... there are a few exceptions besides 1:
?100 mod 10
 0
etcetera

I want to know what's wrong with int(dCantidadRegistro) sometimes when dCantidadRegistro = 1
Floating point errors are a fact of life in any programming language.

Sometimes the result of a calculation yields a number that is very close to the *right* answer, but is ever so slightly off.

e.g.
0.999999999999998 and 1.000000000000001 both represent the value of 1 in most situations.

Taking Int() of both numbers will result in different values.

Instead of
If (Int(dCantidadRegistro) = dCantidadRegistro) Then
 maybe use:
If (Int(Round(dCantidadRegistro, 4)) = Round(dCantidadRegistro, 4)) Then

Good Luck!
Also, do not confuse Int() with CInt()

Int() takes the integer portion of the number and discards everything after the decimal.
Int(0.999) = 0
Int(0.55) = 0
Int(1.001) = 1

CInt converts the number to the integer data type.  Rounding up or down as appropriate.
CInt(0.4) = 0
CInt(0.55) = 1
CInt(0.999) = 1
CInt(1.001) = 1
ASKER CERTIFIED SOLUTION
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Very good.
Will it fix the floating point problem? Files just have x number of characters and lines, they don't have 275.000001 characters.
?FileLen(sArchivo)
 275
?Len(RegistroDJVE2)
 275
how come 275 / 275 = floating point when the debugging inmed shows
?LongArchivo / LongConfArchivo
 1

I don't get it
This is way too weird... I had a stopping point and I swear Int(dCantidadRegistro) = dCantidadRegistro returned false and I left the code stopped for a couple of hours and when I came back it would return true. This is way too weir falks or I'm loosing my mind
There shouldnl't be any floating point problems wit the code I posted as it uses whole numbers in the calculations.

Idle_Mind
Out of interest (and I don't if it makes a difference) but doesn't FileLen return a length in Bytes, whereas Len returns the number of characters in a string?

If you are comparing a file length in bytes to a string then, if there are any multi-byte characters (as with some non-English character sets), surely the lengths will be different.

Does it make a difference if you use LenB(...) instead of Len(...), as LenB will return the byte length of the string?
I think you are right jimbobmcgee.  LenB() will account for the padding in Type RegEntradaDJVE2 that will be present in the file but not in memory.

Change:

    LongConfArchivo = Len(RegistroDJVE2)

To:

    LongConfArchivo = LenB(RegistroDJVE2)

Idle_Mind
Len() is correct.
Len() returns the number of characters, LenB() the number of bytes.
In VB strings, one char = 2 bytes (unicode), in files strings are ascii, one byte per character.



As a side note, reading the documentation on FileLen it says:

"If the specified file is open when the FileLen function is called,
the value returned represents the size of the file immediately before it was opened.
Note   To obtain the length of an open file, use the LOF function."
I stand corrected.  (Tested with a small random access file)

Thank you Erick37.

Keep it as:
 LongConfArchivo = Len(RegistroDJVE2)

~IM
> Len() returns the number of characters, LenB() the number of bytes.
> In VB strings, one char = 2 bytes (unicode), in files strings are ascii, one byte per character.

Then, for a true indication of file length (including support for Unicode characters), wouldn't the following be a better comparison

    Dim ByteArray() As Byte
    Dim strText As String
    Dim c As Long
    Dim i As Long
   
    strText = "Hello, Jamie"
    ByteArray() = strText

    c = 0
   
    For i = LBound(ByteArray()) To UBound(ByteArray())
   
        Me.Print ByteArray(i); "("; ByteArray(i) > 0; ")";
        If ByteArray(i) > 0 Then c = c + 1
       
    Next i

instead comparing c with FileLen(file), or is Unicode handled differently in file I/O?
I tried using LOF(iFreeFile):
LongArchivo = LOF(iFreeFile)
LongConfArchivo = Len(RegistroDJVE2)
dCantidadRegistro = (LongArchivo / LongConfArchivo)

LongArchivo = 275 and LongConfArchivo = 275 but dCantidadRegistro = 0,25 but when I go back and run again dCantidadRegistro = (LongArchivo / LongConfArchivo), dCantidadRegistro calculates correctly and returns 1.

Idle_Mind: Your code is ok but it does work for me because it looses it's porpose... I mean this code is supposed to verify if the file I am reading is well written and I do that comparing to the structure so if you make a int division then you will never know if the amount of items are correctly (integer and with no decimals)
pwebonline,

That is exactly what the my code does!  Let me give you an example.  Suppose you have this definition:

    Public Type myStructure
        strA As String * 10
        strB As String * 10
        strC As String * 10
    End Type

The length of this structure is 30 bytes.  If you wrote one record to the file, it would be 30 bytes long.  If you wrote two records to the file it would be 60 bytes long.  If you wrote three records to the file, it would be 90 bytes long....

So, for a valid file, the length will be a multiple of the length of your structure:

    (Length of File) = (# of records) * (Length of Structure)

Now let's solve this equation for (# of records):

    (# of records) = (Length of File) / (Length of Structure) ' Regular Divison (Forward Slash)

I understand that you already know this.  But..if you change it from regular division to integer division:

    (# of records) = (Length of File) \ (Length of Structure) ' Integer Divison (Backslash)

This will give you the number of WHOLE records in your files.  If 3.14150 records were written, it would return 3.  Now mulltiply the number of whole records in the file by the length of your structure.  If this value matches the actual length of the file then you know you have a valid file.  If they don't match, you know you have a corrupt record or a partial record on the end of the file.

Going back to the example.  Let's suppose you have an invalid file with 3.5 records in it.  This would give you a length of 105 bytes  (For this to be a valid file, it would need 4 records for a length of 120 bytes).  Now consider the code I gave you:

    Dim myStruct As myStructure
    Dim wholeRecords As Long
    Dim fileLength As Long
    Dim structureLength As Long
    Dim validFileLength As Long

    fileLength = FileLen("c:\someFile.txt") ' 105 bytes
    structureLength = Len(myStruct) ' 30 bytes

    ' This will give you the value 3
    wholeRecords = fileLength \ LongConfArchivo ' <--- Integer Division (Backslash, not Forward Slash)
   
    validFileLength = wholeRecords * structureLength ' 90 bytes

    If validFileLength = fileLength  Then ' 90 is not equal to 105

        'Everything is ok so process file

    Else

        'Something is wrong with either the file or the structure                

    End If

Now let's suppose you have a valid file.  What if the file had exactly 3 records in it?  The file length would be 90.  The inumber of whole records returned by the integer division would be 3.  If you multiply the length of the structure (30) by the number of whole records (3), you would get 90.  This matches the actual file length and therefore the file is valid.

I hope I have explained this clearly enough to demonstrate the concept.  If not, I will answer any questions you may have.

Regards,

Idle_Mind
Sorry, this:

    ' This will give you the value 3
    wholeRecords = fileLength \ LongConfArchivo ' <--- Integer Division (Backslash, not Forward Slash)

should be:

    ' This will give you the value 3
    wholeRecords = fileLength \ structureLength ' <--- Integer Division (Backslash, not Forward Slash)

~IM