• Miércoles 15 de Mayo de 2024, 23:08

Autor Tema:  problema con decimales  (Leído 2719 veces)

oscuros

  • Nuevo Miembro
  • *
  • Mensajes: 5
    • Ver Perfil
problema con decimales
« en: Jueves 18 de Diciembre de 2008, 12:56 »
0
Buenas, verán tengo un programa donde en una fórmula me aparece con dos decimales, pero lo que necesito es que si el decimal está entre 0 i 0.25 que redondee hacia abajo, si el decimal está entre 0.25 i 0.75 que redondee a 0.50 y que si está por encima de 0.75 que redondee para arriba.
Alguién sabe como puedo hacer eso?

Muchas gracias

oscuros

  • Nuevo Miembro
  • *
  • Mensajes: 5
    • Ver Perfil
Re: problema con decimales
« Respuesta #1 en: Viernes 19 de Diciembre de 2008, 10:00 »
0
Ahora que me lo miro creo q no me he explicado bien, lo d ls decimales no es para la formula sino para el resultado de ésta. Que me aparece con dos decimales y lo que quiero es q me redondee los decimales al numero entero anterior o siguiente o redondee al numero ,5.

Gracias

Nebire

  • Miembro HIPER activo
  • ****
  • Mensajes: 670
    • Ver Perfil
Re: problema con decimales
« Respuesta #2 en: Martes 30 de Diciembre de 2008, 11:30 »
0
Hay que crear unas pequeñas funciones que nos den el formato que necesitemos, te pongo un ejemplo con 3 funciones (sobrecargadas) que soluciona estos problemas... y de ,los que puedes deducir el camino a seguir para ajustarlo a otras ideas.

En esta primera función, se pasa el valor que se desea formatear y se pasa el límite que fija redondearse arriba o abajo. en el código del 'botón 1'  se muestra ejemplos y el resultado de llamadas con distintos valores.
Código: Visual Basic
  1.  
  2. ' fijado un limite lo que supera esos decimales devuelve el siguiente entero
  3.     ' si no supera el límite devuelve el entero
  4.     Public Function Redondear(ByVal cifra As Double, ByVal Limite As Double)
  5.         Dim resto As Single = cifra Mod 1
  6.  
  7.         If resto <= Limite Then
  8.            Return Math.Floor(cifra)
  9.         Else
  10.             Return Math.Ceiling(cifra)
  11.         End If
  12.     End Function
  13.  
  14. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  15.         ' usando 1 limite:
  16.         MsgBox(Redondear(234.54, 0.4)) ' devuelve 235
  17.         MsgBox(Redondear(234.54, 0.6)) ' devuelve 234
  18.         MsgBox(Redondear(234.54, 0.54)) ' devuelve 234
  19.         MsgBox(Redondear(234.99, 0.98)) ' devuelve 235
  20. End Sub
  21.  
  22.  

En la siguiente función, que es una sobrecarga de la anterior se pasan 2 límites, uno inferior y otro superior si el límite superior es un punto superior al limite inferior se comporta como la función anterior también sucede este comportamiento si el límite superior es igual o menor que el limite inferior, pero si el límite superior dista del limite inferior se devuelve el valor que se introdujo sin formatear...

El 'botón 2' prueba el uso de esta función con algunos ejemplos.
Código: Visual Basic
  1.  
  2. ' ahora se usan 2 límites, si es menor o igual al limite inferior devuelve el entero anterior,
  3.     '   si es mayor o igual que el limite superior devuelve el entero superior
  4.     '   si está entre ambos límites devuelve el valor entrado.
  5.     Public Function Redondear(ByVal cifra As Double, ByVal LimiteInf As Double, ByVal LimiteSup As Double)
  6.         Dim resto As Single = cifra Mod 1
  7.  
  8.         If resto <= LimiteInf Then
  9.             Return Math.Floor(cifra)
  10.         ElseIf resto >= LimiteSup Then
  11.             Return Math.Ceiling(cifra)
  12.         Else
  13.             Return cifra
  14.         End If
  15.     End Function
  16.  
  17. Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
  18.     ' usando 2 limites: superior e inferior
  19.         MsgBox(Redondear(234.54, 0.4, 0.6)) ' devuelve 234.54
  20.         MsgBox(Redondear(234.54, 0.5, 0.51)) ' devuelve 235
  21.         MsgBox(Redondear(234.34, 0.5, 0.51)) ' devuelve 234
  22.         MsgBox(Redondear(234.34, 0.25, 0.26)) ' devuelve 235
  23.         MsgBox(Redondear(234.54, 0.5, 0.2)) ' devuelve 235 si limite superior es menor que limite inferior este caso es equivalente a usar 1 sólo límite.
  24. end sub
  25.  
  26.  

En la 3ª función que es sobrecarga de las anteriores se muestra justo el caso que indicas. Esto es usamos 3 límites. Si la parte decimal es igual o menor que el limite inferior se redondea al entero menor más próximo, si la parte decimal es igual o superior al límite superior edondea al entero mayor más próximo y si no es ningúno de los casos anteriores se devuelve la parte entera + el límite medio fijado.

Nota que el límite medio no realiza comprobaciones de si está en un rango dado, esto sólo sería necesario si quisiéramos indicar 4 límites. Lo que hace es que si no es ninguno de los 2 casos anteriores. En tu pregunta los valores para estos límites deberían ser:  redondear( valor, 0.25,0.75,0.5  )
El 'botón 3' muestra unos ejemplos del uso de esta función y el resultado a cada caso.
Código: Visual Basic
  1.  
  2. ' como en el caso anterior, pero ahora cuando no está en márgenes inferior o superior devuelve el punto medio indicado...
  3.     Public Function Redondear(ByVal cifra As Double, ByVal LimiteInf As Double, ByVal LimiteSup As Double, ByVal LimiteMed As Double)
  4.         Dim resto As Single = cifra Mod 1
  5.        
  6.         If resto <= LimiteInf Then
  7.             Return Math.Floor(cifra)
  8.         ElseIf resto >= LimiteSup Then
  9.             Return Math.Ceiling(cifra)
  10.         Else
  11.             Return Math.Floor(cifra) + LimiteMed
  12.         End If
  13.     End Function
  14.  
  15.     Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
  16.        ' usando los 3 límites: supeior inferior y medio
  17.         MsgBox(Redondear(234.54, 0.4, 0.6, 0.5)) ' devuelve 234.5
  18.         MsgBox(Redondear(234.62, 0.5, 0.6, 0.5)) ' devuelve 235
  19.         MsgBox(Redondear(234.50, 0.5, 0.6, 0.3)) ' devuelve 234  pero un valor de de 234.51 devolvería 234.3
  20.         MsgBox(Redondear(234.34, 0.25, 0.6, 0.3)) ' devuelve 234.3
  21.     End Sub
  22.  
  23.  

Math.Ceiling, Math.Floor y math.Round son funciones de redondeo, la 1ª devuelve el entero siguiente al actual, el 2ª ídem pero aplicando el inferior, el 3ª devuelve el entero más próximo, hemos usado floor y ceiling para contruir nuestro propio formato de redondeo. La traducción literal de ceiling y floor es techo y suelo, para que no parezca algo 'raro' o misterioso...

Si tienes que hacer bastantes más cosas al respecto podría interesarte realizar una pequeña clase de matematicas donde vas reuniendo este tipo de funciones.

Además las funciones no son fijas en el sentido de que en cada llamada podemos fijar unos límites distintos. si creas una clase podrías especificar unas propiedades con dichos límites (que podrían definirse con el constructor de la instancia) y así no pasar en cada llamada a la función estos valores sino que los recogería internamente... eso queda como ejercicio para tí.

Cualquier duda que tengas pregunta...
«Ma non troppo»
----> ModoVacaciones = False<----

oscuros

  • Nuevo Miembro
  • *
  • Mensajes: 5
    • Ver Perfil
Re: problema con decimales
« Respuesta #3 en: Lunes 12 de Enero de 2009, 09:59 »
0
Muchas gracias pero al final lo he hecho con otro codigo.

    Private Function getRedondeo(ByVal numero As Decimal) As Decimal
        Dim salida As Decimal = 0
        Dim i As Integer = 0
        Dim b As Integer = 0

        Dim aux As String = Format(numero, "#.00")
        aux = aux * 100
        Dim parteDecimal As Integer = aux Mod 100
        Dim a As String = numero.ToString
        Try
            For i = 0 To a.Length - 1
                If a.Chars(i) = "," Then
                    b += 1
                End If
                If a.Chars(i) <> "," And b = 0 Then
                    i += 1
                End If
            Next

            If parteDecimal <> 0 Then
                a = a.Substring(0, i)

                Dim parteEntera As Integer = a

                Select Case parteDecimal
                    Case 0 To 25
                        parteDecimal = 0
                    Case 26 To 75
                        parteDecimal = 50
                    Case 76 To 99
                        parteDecimal = 0
                        parteEntera += 1
                End Select

                aux = parteEntera & "," & parteDecimal

                salida = CType(aux, Decimal)

                Return salida
            Else
            End If
        Catch ex As Exception
        End Try

    End Function

Nebire

  • Miembro HIPER activo
  • ****
  • Mensajes: 670
    • Ver Perfil
Re: problema con decimales
« Respuesta #4 en: Viernes 16 de Enero de 2009, 00:01 »
0
Me parece muy bien que busques una solución alternativa.

Sin embargo al caso presente le pongo 2 'peros'.

El primero es que siempre que haya que tratar un número como caracteres (tal como haces convirtiéndolo en una batriz de chars), implica una pérdida importante de eficiencia. Esto por sí sólo, es razón suficiente para descartar un algoritmo basado en esa manera, salvo que no exista un modo alternativo realmente eficaz. En el caso presente no está justificado, las funciones que te he expuesto son muy sencillas y simples. Podrías perfectamente remplazar la parte del bloque if ---then  por el bloque select case, perfectamente, para casos más o menos complejos y eso sería todo. Hablando claramente formatear un número a través de caracteres es una cagada, en el 99% de los casos.

El segundo 'pero' es que los bloques try .. catch aunque son muy útiles, hay que saber cuando usarlos. Rresulta una estupidez esperar un error donde no debe haberlo, entonces algo como
Código: Visual Basic
  1.  
  2. dim r,s,t as string
  3.     s="Ho" : t= "la"
  4.     try
  5.         r = s & t
  6.         messagebox.show r
  7.     catch ex as exception
  8.         messagebox.show ex.exception
  9.     finally
  10.         messagebox.show " Mundo."
  11.     end try
  12.  
  13.  
.... es una solemne estupidez... Hay que utilizar bloques try..catch , pero sólo cuando pueda generarse un error que no podamos controlar, porque las funciones llamadas o los parámetros utilizados queden fuera de nuestro control. Te puedo dar una regla sobre cuando usar bloques try..catch; es como usar un guardaespaldas, pregúntate: cuando se usa un guardaespaldas ?... cuando existe una amenaza, si no la hay, sobra el guardaespealdas, lo mismo se aplica para el try ...catch. Para ir desde la cama al baño, no necesitas un guardaespaldas, no hay amenaza.

Por último un pero menor... es el nombre de la función... getRedondeo ... No sería más sencillo llamarlo Redondear ?, getLoquesea lo que indica es que se obtiene lo que sea, dicho de otro modo, get es tomar algo 'ya almacenado' y si fuera el caso previamente calculado. Cuando algo no está almacenado, sino que debe hacerse, debe calcularse, porque es una acción, es una acción entonces en estos casos es mejor usar un verbo, así si prefieres usar terminología inglesa podrías usar 'DoRound', 'Do' hacer... o 'CalculateRound'. Si tienes que coget una cerveza de la nevera usa GetCerveza(nevera), pero si la cerveza debe elaborarse entonces usa DoCerveza(1 litro, 5kg cebaza, 200gr malta,....).. No es lo mismo ir a una pastelería y pedir una tarta GetTarta (tomar tarta), que ir a pedir el encargo de una tarta (una tarta con unas especificaciones que implica que deba reunirse los ingredientes y hacerla), en este caso lo llamaríamos DoTarta (hacer la tarta y romarla).

Son sólo indicaciones, seguirlas o no ya es cosa tuya.
«Ma non troppo»
----> ModoVacaciones = False<----