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
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,
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
ASKER
Does it make a difference?... I mean, is there any chance mod will fail too?
ASKER
Mod fails too. I don't understand the inmed mode shows:
?FileLen(sArchivo)
275
?Len(RegistroDJVE2)
275
What is wrong?
?FileLen(sArchivo)
275
?Len(RegistroDJVE2)
275
What is wrong?
ASKER
Mod is not good....
?FileLen(sArchivo) mod 1.2
0
?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
>?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
ASKER
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
?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.
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
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,".
----- OK
ASKER
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
?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(dCantidadRegist ro, 4)) = Round(dCantidadRegistro, 4)) Then
Good Luck!
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(dCantidadRegist
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
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Very good.
ASKER
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
?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
ASKER
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
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?
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
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."
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
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?
> 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?
ASKER
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)
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
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")
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
' 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
if (FileLen(sArchivo) mod Len(RegistroDJVE2) = 0) then
'etcetera