Números enteros en VBA

Iniciado por isam, 8 Noviembre 2020, 20:32 PM

0 Miembros y 1 Visitante están viendo este tema.

isam

Holaa,


Apenas estoy iniciando con el mundo de programación y tengo un problema con un código. Acabo de hacer un código para un formulario el cual necesito que los números que se ingresen sean mayores de 0 y menores de 50, y a la vez sean enteros. Mi problema está en poder realizar el código en la parte de números enteros ya que no logro configurarlo. Adjunto el código que realicé:

Código (csharp) [Seleccionar]

Private Sub TextBox2_afterupdate()
If Val(TextBox2) < 1 Or Val(TextBox2) > 50 Or Val(TextBox2) Mod 1 = 0 Then
       MsgBox ("Dato incorrecto")
       TextBox2.Value = ""
   Else
       nota1 = TextBox2
End If
End Sub



Les agradecería mucho

[MOD] Usar las etiquetas GeSHi para publicar código.

EdePC

Los operadores de división y residuo en VisualBasic es un lío, has de saber que el operador Mod siempre devuelve un número entero por lo que no te va ha servir para hallar un residuo decimal.

Una opción es hallar el residuo a mano con la fórmula:

Código (vb) [Seleccionar]
residuo = Dividendo - (divisor * cociente_entero)

- Puedes usar Fix(numero) para obtener el Entero de un número decimal, ya que cInt(), Int(), etc también redondean XD, solo Fix() obtiene el Entero tal cual.

Quedaría así:

Código (vb) [Seleccionar]
If Val(TextBox2) < 1 Or Val(TextBox2) > 50 Or Val(TextBox2) - Fix(Val(TextBox2) / 1) <> 0 Then

Otra opción más simple es comparar el número con su entero, cosa que si ambos son enteros deben ser iguales, caso contrario uno será decimal y el otro entero:

Código (vb) [Seleccionar]
If Val(TextBox2) < 1 Or Val(TextBox2) > 50 Or Val(TextBox2) <> Fix(Val(TextBox2)) Then

Referencias:

https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/operators/mod-operator
https://docs.microsoft.com/en-us/office/vba/language/concepts/getting-started/type-conversion-functions#cint-function-example
https://stackoverflow.com/questions/10631992/function-to-remove-the-decimal-places

Serapis

#2
Es importante saber primero si para tí es válido la introducción de valores decimales o no. Es decir si es válido que pueda truncarse o si debe ser rechazado...

Supongamos que es aceptable truncar un valor con decimales al valor entero, tú código vendría a ser:
Código ( vbnet) [Seleccionar]

Private Sub TextBox2_afterupdate()
   dim k as integer

   k = val(textbox2.value)  ' el valor queda truncado al tipo de datos de destino... un entero al caso.
   If ((k < 1) Or (k > 50))  Then
       MsgBox ("Dato incorrecto")
       TextBox2.Value = ""
   Else
       nota1 = k.tostring  ' si es que quieres un string.
   End If
End Sub


Si para tí es inaceptable truncar el valor entonces el código cambia ligeramente:
Código ( vbnet) [Seleccionar]

Private Sub TextBox2_afterupdate()
   dim k as single  ' nota el cambio de tipo...

   k = val(textbox2.value)  ' el valor queda truncado al tipo de datos de destino... aora un 'single'.
   If ((k < 1) Or (k > 50) or ((k mod 1)<>0))  Then
       MsgBox ("Dato incorrecto")
       TextBox2.Value = ""
   Else
       nota1 = k.tostring  ' si es que quieres un string.
   End If
End Sub


OJO: la instrucción 'Val' acepta el '.' como separador decimal, pero no ',' luego para un texto como: val("123,45") devuelve 123'0
En cambio Convert.ToSingle("123.45") tiene en cuenta el idioma y lo interpreta como separador de miles (si es español), luego devuelve 12345'0.

Y en cualquier caso, pon mensajes más específicos... 'dato incorrecto', no determina que se espera que se introduzca... redacta mensajes específicos y al caso mejor diferenciar cuando el fallo es fuera de rango de cuando contiene decimales. Por ejemplo (en VB-NET):
Código ( vbnet) [Seleccionar]

   Private Sub TextBox1_Validating(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
       Dim k As Single

       k = Val(TextBox1.Text)
       'k = Convert.ToSingle(TextBox1.Text)
       If ((k Mod 1) <> 0) Then
           MessageBox.Show("Debe ser un número entero. No se admiten decimales...")
       ElseIf ((k < 0) Or (k > 50)) Then
           MessageBox.Show("El valor a introducir debe estar en el rango 1-50.")
       Else
           nota1 = k.ToString ' ok...
       End If
   End Sub


Y aprovechando que estás aprendiendo... un consejo... utiliza los paréntesis para encerrar el orden de evaluación de las expresiones, es muy fácil para los que empiezan cometer este tipo de errores que luego no logran ver donde está le fallo, porque todo aparentemente está bien.

Aunque de entrada tienes un error semántico...
Citar... or Val(TextBox2) Mod 1 = 0
Se supone que si el resto es 0, entonces se trata de un número entero, para que 'caiga en ese saco' con los condiionantes indicando fuera de rango unidos por or, el operador debe ndicar distinto de 0, no igual a 0...

resto = (k mod 1)
Si (k < 1) o (k > 50) o (resto <> 0) ...
' o si no cambialo a la inclusión del rango:
Si (k >= 0) y (k <= 50) y (resto = 0) ...

' Ambas expresiones son idénticas en cuanto a semántica.

Cita de: EdePC en  9 Noviembre 2020, 03:43 AM
Los operadores de división y residuo en VisualBasic es un lío,
?????...

Cita de: EdePC en  9 Noviembre 2020, 03:43 AM
has de saber que el operador Mod siempre devuelve un número entero por lo que no te va ha servir para hallar un residuo decimal.
Esto es falso... en todas las versiones de visual Basic, el operador mod está capacitado para devolver un valor decimal. Otra cosa es que VB trunca de modo automático el dato devuelto al tipo de destino, cuando es posible.

Además cuando existiere algún problema el operador mod puede ser sobrecargado, para que se comporte como uno pudiera esperar/desear...  (por ejemplo hacer un modulo a un tipo de datos de una estructura 'area-point' con decimales).

Operador mod (en español, que existe):
https://docs.microsoft.com/es-es/dotnet/visual-basic/language-reference/operators/mod-operator

EdePC

Cita de: EdePC en  9 Noviembre 2020, 03:43 AM
Los operadores de división y residuo en VisualBasic es un lío, has de saber que el operador Mod siempre devuelve un número entero por lo que no te va ha servir para hallar un residuo decimal.

Según las pruebas que hice en un Excel 2013 VBA, en su ventanita de Inmediato (Ctrl + G) tuve feos problemas leyendo la documentación ya que al momento de convertir un dato Decimal a Entero tenia comportamientos diferentes dependiendo de que si era positivo, negativo, la función que convierte, etc. La documentación lo dice, pero es un poco problemático.

Código (vb) [Seleccionar]
? 2.0 Mod 0.2
` Error división entre 0

? 2D Mod 0.2D
` Error de sintaxis

? 3.6 Mod 1
0

? 3.6 Mod 1.0
0

? 3.6 Mod 2.3
0

? 32.7 \ 1
33

? 32.7 / 1
32,7


Es medio especial el VBA en el tratamiento de Mod, división y conversión a enteros. Tampoco he logrado obtener un residuo decimal como en otros lenguajes, a parte que según la documentación dice que primero redondea los operadores antes de hacer la operación, Dividendo Mod 1 se utiliza en otros lenguajes para saber si el número dado es Entero o Decimal, pero en VBA el operador Mod siempre me ha devuelto un Entero redondeado XD

Para el ejemplo:

Código (vb) [Seleccionar]
dim k as integer
k = val(textbox2.value)  ' el valor queda truncado al tipo de datos de destino... un entero al caso.


- Si se le pone un 2.8, k termina valiendo 3 porque se redondea

Para el ejemplo:
Código (vb) [Seleccionar]
Dim k As Single  ' nota el cambio de tipo...

k = Val(TextBox2.Value)  ' el valor queda truncado al tipo de datos de destino... aora un 'single'.
If ((k < 1) Or (k > 50) Or ((k Mod 1) <> 0)) Then


- Si se le pone un 2.8, termina dando por válido el valor porque k Mod 1 devuelve 0

Obviamente es asunto de VBA, ya que por los ejemplos mostrados si debe de funcionar en Visual Basic 6.0 y .NET. O es mi versión de Excel XD