VB6 – La eterna lucha con el símbolo decimal (el punto o la coma)

Qué bonito es el mundo de la informática cuando todo esta estandarizado.

Pero a veces es imposible, sobre todo si trabajamos con sistemas de distintos países o idiomas.
Un pequeño símbolo que nos separa la parte entera de la parte fraccional de un número decimal, puede tirarnos por los suelo todo un programa. Y solo por no haber tenido en cuenta los idiomas de los sistemas con los que íbamos a trabajar.

Sé que una cosa así puede parecer una tontería, pero una de las cosas que aprendí (y sufrí) hace ya bastante tiempo fue que una confusión en un punto decimal puede tener resultados catastróficos.

Por ejemplo, como un número decimal como el “-1,574”, después de ser procesado, se puede convertir en “-1574”.
Y claro, no es lo mismo bajar una profundidad de -1,574 mm que bajar a -1574 mm.
Y lo mismo si estuviéramos hablando de dinero, no es lo mismo un “agujero” de -1,574€ o $ que -1574€ o $. ¡Menudo desastre! (haz un programa así y dale los informes resultantes a tu jefe, a ver qué cara se le pone).

¿Dónde podemos encontrar el problema?

Algunos casos practicos:

– Cuando trabajamos con número que son procesados en sistemas operativos con distintos idiomas.
Por ejemplo, programamos una aplicación en un sistema español.
Esta aplicación crea ficheros con número con la coma decimal.
Seguidamente estos ficheros son procesados por la misma aplicación pero en un sistema inglés. El sistema inglés tiene un formato de decimales distinto y surge el error de conversión.

– Cuando obtenemos datos de una base de datos.
Por ejemplo, nosotros programamos en un Windows con idioma “español (España)” con el formato estándar, es decir el símbolo decimal es la coma “,”.
Y realizamos una consulta a una bases de datos que tiene el idioma de instalación en inglés, es decir el símbolo decimal es el punto “.”.
Depende de cómo tengamos la conexión, el tipo de datos, etc. pero puede que los datos numéricos los recibamos como texto y necesitemos una conversión. De nuevo tenemos un error de conversión.

– Al manejar un folio Excel con VBA (Visual Basic for Applications).
Por razones similares. Sobre todo con sistemas en castellano (Idioma: español – España), en los folios Excel el separador decimal se identifica con una coma “,”; al contrario de lo que sucede en otros idiomas, como el inglés, italiano, etc. que se identificar con un punto “.”.
Por tanto, mucho ojo al formato y el valor de las celdas.

– Al lanzar aplicaciones de otros idiomas.
Un caso frecuente, hacemos una aplicación en VB (en español) para automatizar una proceso.
Esta aplicación construye un pequeño script y lanza una aplicación que lee dicho script (normalmente, una aplicación en inglés).
¿Qué sucede? Pues que si no hemos tenido cuidado y en el script se pasan datos numéricos, seguro que el script estará en “formato español” y la aplicación inglesa no lo interpretara correctamente, ya que necesita el script en “formato inglés”.

“Sí…. Ya sé…. Hay funciones del visual que hacen esto.” Que nos convierten los números a texto y viceversa.

Sí es cierto, la funciones de conversión de tipos:

CBool( )
CByte( )
CCur( )
CDate( )
CDbl( )
CDec( )
CInt( )
CLng( )
CSng( )
CStr( )
CVar( )

Pero a mi hace tiempo que se me quedaron cortas y lo único que me servían eran para tener otro riesgo que podría saltar cualquier momento. Y sinceramente… dependiendo de qué tipo de aplicación estemos desarrollando un fallo tonto como una confusión en el símbolo decimal puede poner en alto riesgo la integridad de personas.

Mi solución, como siempre digo, es mi solución, la que he encontrado, la que me ha dado excelente resultados durante años…
Es crearme mis propias funciones de conversión de número a texto y de texto a número. Y así poder procesar los texto/números a mi antojo, pudiendo pasarles los formatos de entrada que quiera y obtener los formatos de salida deseados.

Puede ser duro al principio y puede que haya que depurarlas hasta que estemos seguros de su correcto funcionamiento. Pero una vez conseguidas son unas funciones “para toda la vida”.
Yo, en concreto, solo tengo 2 funciones. Una para convertir un “String” a “Double” y otra para pasar de “Double” (o número, en general) al “String”. Y sinceramente, lo sufrí tanto que ahora solo uso estas funciones y utilizar las funciones de conversión estándar me cuesta muchísimo.

‘————————————————-§§§—-‘
‘ FUNCION PARA CONVERTIR UN NUMERO EN TEXTO
‘————————————————-§§§—-‘

Public Function De_Num_a_Tx_01(ByVal lNumero As Double, _
                               Optional ByVal bEntero As Boolean = False, _
                               Optional ByVal nDecimales As Integer = 3) As String
    '-------------------------------------------------§§§----'
    ' FUNCION PARA CONVERTIR UN NUMERO EN TEXTO
    '-------------------------------------------------§§§----'
    '
    On Error GoTo Fin
    '
    Dim sNumero As String
    Dim nLong1 As Integer
    Dim nCont1 As Integer
    '
    If bEntero = True Then
        sNumero = CStr(Format(lNumero, "########0"))
        ''
    Else
        Select Case nDecimales
            Case -1: sNumero = CStr(Format(lNumero, "########0.#########"))
            Case 1: sNumero = CStr(Format(lNumero, "########0.#"))
            Case 2: sNumero = CStr(Format(lNumero, "########0.0#"))
            Case 3: sNumero = CStr(Format(lNumero, "########0.00#"))
            Case 4: sNumero = CStr(Format(lNumero, "########0.000#"))
            Case 5: sNumero = CStr(Format(lNumero, "########0.0000#"))
            Case 6: sNumero = CStr(Format(lNumero, "########0.00000#"))
            Case 7: sNumero = CStr(Format(lNumero, "########0.000000#"))
            Case 8: sNumero = CStr(Format(lNumero, "########0.0000000#"))
            Case 9: sNumero = CStr(Format(lNumero, "########0.00000000#"))
            Case 9: sNumero = CStr(Format(lNumero, "########0.00000000#"))
            Case 10: sNumero = CStr(Format(lNumero, "########0.000000000#"))
            Case 11: sNumero = CStr(Format(lNumero, "########0.0000000000#"))
            Case 12: sNumero = CStr(Format(lNumero, "########0.00000000000#"))
            Case Else: sNumero = CStr(Format(lNumero, "########0.00#"))
        End Select
        ''
    End If
    '
    nLong1 = Len(sNumero)
    '
    For nCont1 = 1 To nLong1
        If Mid$(sNumero, nCont1, 1) = "," Then Mid(sNumero, nCont1, 1) = "."
        ''
    Next nCont1
    '
    If bEntero = True Then
        De_Num_a_Tx_01 = sNumero
        ''
    ElseIf InStr(sNumero, ".") > 0 Then
        If (Len(sNumero) = InStr(sNumero, ".")) And (nDecimales = -1) Then
            De_Num_a_Tx_01 = Mid$(sNumero, 1, InStr(sNumero, ".") - 1)
            ''
        Else
            De_Num_a_Tx_01 = sNumero
            ''
        End If
        ''
    Else
        De_Num_a_Tx_01 = sNumero & ".0"
        ''
    End If
    '
Exit Function
'
Fin:
    De_Num_a_Tx_01 = "###.###"
    ''
End Function

‘————————————————-§§§—-‘
‘ FUNCION PARA CONVERTIR UN TEXTO EN NUMERO DECIMAL
‘————————————————-§§§—-‘

Public Function De_Txt_a_Num_01(ByVal sTexto As String, _
                                   Optional ByVal nDecimales As Integer = 3, _
                                   Optional ByVal sP_Formato_Decimal As String = "") As Double
    '-------------------------------------------------§§§----'
    ' FUNCION PARA CONVERTIR UN TEXTO EN NUMERO DECIMAL
    '-------------------------------------------------§§§----'
    '
    Dim bCte2 As Boolean
    '
    Dim nContador1 As Integer
    Dim nContador2 As Integer
    Dim nLong_Total As Integer
    Dim nPos_Punto As Integer
    Dim nCte1 As Integer
    Dim nDecimal As Integer
    '
    Dim lNumeruco As Double
    '
    Dim sNumero As String
    Dim sL_Aux_01 As String
    '
    Dim sL_Array_Pto_01() As String
    Dim sL_Array_Coma_01() As String
    '
    On Error GoTo Error_Numero
    '
    '-------------------------------------------------§§§----'
    Select Case sP_Formato_Decimal
        Case "."    ' USAMOS "." COMO SEPARADOR DE DECIMALES
                    ' Y LA "," LA ELIMINAMOS
            sL_Array_Pto_01 = Split(sTexto, ".")
            sL_Array_Coma_01 = Split(sTexto, ",")
            '
            sL_Aux_01 = ""
            For nContador1 = LBound(sL_Array_Coma_01) To UBound(sL_Array_Coma_01)
                sL_Aux_01 = sL_Aux_01 & sL_Array_Coma_01(nContador1)
                ''
            Next nContador1
            '
            sTexto = sL_Aux_01
            ''
        Case ","    ' USAMOS "," COMO SEPARADOR DE DECIMALES
                    ' Y EL "." LE ELIMINAMOS
            sL_Array_Pto_01 = Split(sTexto, ".")
            sL_Array_Coma_01 = Split(sTexto, ",")
            '
            sL_Aux_01 = ""
            For nContador1 = LBound(sL_Array_Pto_01) To UBound(sL_Array_Pto_01)
                sL_Aux_01 = sL_Aux_01 & sL_Array_Pto_01(nContador1)
                ''
            Next nContador1
            '
            sTexto = sL_Aux_01
            ''
    End Select
    '-------------------------------------------------§§§----'
    '
    lNumeruco = 0
    '
    If nDecimales >= 0 Then
        nDecimal = nDecimales
        ''
    Else
        nDecimal = 3
        ''
    End If
    '
    sTexto = Trim(sTexto)
    '
    If InStr(1, sTexto, "-") > 0 Then
        'Es un numero negativo
        bCte2 = True
        sTexto = Mid$(sTexto, 2)
        ''
    ElseIf InStr(1, sTexto, "+") > 0 Then
        'Es un numero positivo (con signo)
        bCte2 = False
        sTexto = Mid$(sTexto, 2)
        ''
    Else
        'Es un numero positivo
        bCte2 = False
        ''
    End If
    '
    nLong_Total = Len(sTexto)
    '
    For nContador1 = 1 To nLong_Total
        If Mid(sTexto, nContador1, 1) = "," Then Mid(sTexto, nContador1, 1) = "."
        ''
    Next nContador1
    '
    If InStr(1, sTexto, ".") <= 0 Then sTexto = sTexto & ".0"
    '
    nPos_Punto = InStr(1, sTexto, ".")
    '
    nContador2 = 0
    For nContador1 = 1 To nLong_Total
        If Mid$(sTexto, nContador1, 1) <> "." Then
            'No estamos en el caracte "."
            If nContador1 < nPos_Punto And nPos_Punto <> 0 Then
                nCte1 = 1
                ''
            Else
                nContador2 = nContador2 + 1
                nCte1 = 0
                ''
            End If
            '
            sNumero = Mid$(sTexto, nContador1, 1)
            '
            If nContador2 > nDecimal Then
                If sNumero > 5 Then lNumeruco = lNumeruco + (CSng(1) * (10 ^ (nPos_Punto - nContador1 - nCte1 + 1)))
                nContador1 = nLong_Total
                ''
            Else
                lNumeruco = lNumeruco + (CSng(sNumero) * (10 ^ (nPos_Punto - nContador1 - nCte1)))
                ''
            End If
            ''
        End If
        ''
    Next nContador1
    '
    If bCte2 = True Then
        De_Txt_a_Num_01 = (-1) * lNumeruco
        ''
    Else
        De_Txt_a_Num_01 = (1) * lNumeruco
        ''
    End If
    '
    If (nDecimales >= 0) Then De_Txt_a_Num_01 = Round(De_Txt_a_Num_01, nDecimales)
    '
Exit Function
'
Error_Numero:
    '
    '-------------------------------------------------§§§----'
    ' ERROR DE NUMERO
    '-------------------------------------------------§§§----'
    '
    De_Txt_a_Num_01 = -1.75E+308
    ''
End Function
Anuncios

Acerca de Robert Ale
Soy yo un simple tipo que quiere estar en la red... aunque no se si lo conseguiré algún día...

11 Responses to VB6 – La eterna lucha con el símbolo decimal (el punto o la coma)

  1. Pingback: Articulo Indexado en la Blogosfera de Sysmaya

  2. pepe says:

    Bastante bien, pcaiencia amigo!!

  3. Fernando says:

    Muy bueno, me ayudo mucho gracias!

  4. Sirio says:

    Buena… Aun despues de un tiempo!

  5. INCREIBLE NO HAY COMO AGRADECERTELO!!! ES LO MEJOR!!! AGRADECIDICIMO!!!

  6. AHH OJO CUANDO LOS NUMEROS SON MAYORES A MIL COMETE ERRORES LA FUNCION!!

    • Robert Ale says:

      Hola, Guillermo!

      Muchas gracias por el comentario anterior!

      ¿Comete errores? Si te refieres a cuando se manejan números del tipo “12.560,20”… es posibles.
      Pero antes habría que procesar un poco el numero (tanto lío de puntos y comas…).

      De todas formas estoy muy interesado que me informéis de cualquier bug que encontréis.

      De nuevo, muchas gracias!!!

  7. Hola, me parece que existe una manera más sencilla:

    ‘Para pasar de double a string, sólo necesitamos una línea de código:
    Num$ = CStr(Num#)

    ‘Para pasar de string a double sólo necesitamos una línea de código (la dividí en tres para que puedan entender 😉 ):
    Num$ = Replace$(Num$, “,”, “.”)
    Num$ = Val(Num$)
    Numero# = CDbl(Num$)

    • Robert Ale says:

      Hola, Javier:

      Gracias por tu comentario.
      Pero si hubiera sido tan sencillo no hubiera hecho toda esta “chifladura”.
      Te prometo que la diversidad de sistemas, idiomas, etc… hacia que las funciones estándar fallaran.
      Extraño verdad?.
      Pero bueno, trabaje mi solución, durante años ha funcionado perfectamente sin retoque y la he querido compartir.

      De todas formas, gracias por compartir tu opinión y saber.
      Muchas gracias! 🙂

  8. Sirio says:

    A mi me funciono perfecto! Gracias

  9. Juan Carlos says:

    Excelente me funciono muy bien

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: