Solved

Bug in vb6 comparing int and double

Posted on 2004-09-21
28
514 Views
Last Modified: 2012-05-05
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
0
Comment
Question by:pwebonline
  • 8
  • 6
  • 4
  • +3
28 Comments
 
LVL 55

Expert Comment

by:Jaime Olivares
Comment Utility
you can use mod operator:

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

0
 

Author Comment

by:pwebonline
Comment Utility
Does it make a difference?... I mean, is there any chance mod will fail too?
0
 

Author Comment

by:pwebonline
Comment Utility
Mod fails too. I don't understand the inmed mode shows:
?FileLen(sArchivo)
 275
?Len(RegistroDJVE2)
 275

What is wrong?
0
 

Author Comment

by:pwebonline
Comment Utility
Mod is not good....
?FileLen(sArchivo) mod 1.2
 0
0
 
LVL 55

Expert Comment

by:Jaime Olivares
Comment Utility
>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
0
 

Author Comment

by:pwebonline
Comment Utility
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
0
 
LVL 55

Expert Comment

by:Jaime Olivares
Comment Utility
mod 1 is the unique exception, you can manage it easily with an If sentence
0
 
LVL 55

Expert Comment

by:Jaime Olivares
Comment Utility
maybe:

If FileLen(sArchivo) < Len(RegistroDJVE2)

BTW. You don't need to use parenthesis in an If sentence in Visual Basic.
0
 
LVL 4

Expert Comment

by:avi247
Comment Utility
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
0
 

Author Comment

by:pwebonline
Comment Utility
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
0
 
LVL 32

Expert Comment

by:Erick37
Comment Utility
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!
0
 
LVL 32

Expert Comment

by:Erick37
Comment Utility
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
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 125 total points
Comment Utility
First of all, this:

    Dim LongArchivo As Integer
    Dim LongConfArchivo As Integer

should be:

    Dim LongArchivo As Long '  <----- Integer isn't very big, and FileLen() returns a Long anyways
    Dim LongConfArchivo As Long ' Again, Integer isn't very big and Len() returns a Long

If your file is good, then LongArchivo will be a multiple of LongConfArchivo.  You can verify that in this way instead:

    Dim quotient As Long
    LongArchivo = FileLen(sArchivo)
    LongConfArchivo = Len(RegistroDJVE2)
    qoutient = LongArchivo \ LongConfArchivo ' <--- Integer Division (Backslash, not Forward Slash)
    If (quotient * LongConfArchivo) = LongArchivo 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

Regards,

Idle_Mind
0
 
LVL 32

Expert Comment

by:Erick37
Comment Utility
Very good.
0
 

Author Comment

by:pwebonline
Comment Utility
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
0
 

Author Comment

by:pwebonline
Comment Utility
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
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
There shouldnl't be any floating point problems wit the code I posted as it uses whole numbers in the calculations.

Idle_Mind
0
 
LVL 16

Expert Comment

by:jimbobmcgee
Comment Utility
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?
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
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
0
 
LVL 32

Expert Comment

by:Erick37
Comment Utility
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."
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
I stand corrected.  (Tested with a small random access file)

Thank you Erick37.

Keep it as:
 LongConfArchivo = Len(RegistroDJVE2)

~IM
0
 
LVL 16

Expert Comment

by:jimbobmcgee
Comment Utility
> 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?
0
 

Author Comment

by:pwebonline
Comment Utility
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)
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
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
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
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
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

When designing a form there are several BorderStyles to choose from, all of which can be classified as either 'Fixed' or 'Sizable' and I'd guess that 'Fixed Single' or one of the other fixed types is the most popular choice. I assume it's the most p…
I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now