CLR: .Net / Mono / Boo / Otros CLR > VB .NET

 problema con decimales

(1/1)

oscuros:
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:
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:
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 --- ' fijado un limite lo que supera esos decimales devuelve el siguiente entero    ' si no supera el límite devuelve el entero     Public Function Redondear(ByVal cifra As Double, ByVal Limite As Double)        Dim resto As Single = cifra Mod 1         If resto <= Limite Then           Return Math.Floor(cifra)         Else            Return Math.Ceiling(cifra)        End If    End Function Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click        ' usando 1 limite:         MsgBox(Redondear(234.54, 0.4)) ' devuelve 235        MsgBox(Redondear(234.54, 0.6)) ' devuelve 234        MsgBox(Redondear(234.54, 0.54)) ' devuelve 234         MsgBox(Redondear(234.99, 0.98)) ' devuelve 235End Sub  
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 --- ' ahora se usan 2 límites, si es menor o igual al limite inferior devuelve el entero anterior,     '   si es mayor o igual que el limite superior devuelve el entero superior    '   si está entre ambos límites devuelve el valor entrado.    Public Function Redondear(ByVal cifra As Double, ByVal LimiteInf As Double, ByVal LimiteSup As Double)        Dim resto As Single = cifra Mod 1         If resto <= LimiteInf Then            Return Math.Floor(cifra)         ElseIf resto >= LimiteSup Then            Return Math.Ceiling(cifra)         Else            Return cifra        End If    End Function Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click    ' usando 2 limites: superior e inferior        MsgBox(Redondear(234.54, 0.4, 0.6)) ' devuelve 234.54        MsgBox(Redondear(234.54, 0.5, 0.51)) ' devuelve 235        MsgBox(Redondear(234.34, 0.5, 0.51)) ' devuelve 234         MsgBox(Redondear(234.34, 0.25, 0.26)) ' devuelve 235        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.end sub  
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 --- ' como en el caso anterior, pero ahora cuando no está en márgenes inferior o superior devuelve el punto medio indicado...    Public Function Redondear(ByVal cifra As Double, ByVal LimiteInf As Double, ByVal LimiteSup As Double, ByVal LimiteMed As Double)        Dim resto As Single = cifra Mod 1                If resto <= LimiteInf Then            Return Math.Floor(cifra)        ElseIf resto >= LimiteSup Then            Return Math.Ceiling(cifra)        Else            Return Math.Floor(cifra) + LimiteMed        End If    End Function     Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click       ' usando los 3 límites: supeior inferior y medio        MsgBox(Redondear(234.54, 0.4, 0.6, 0.5)) ' devuelve 234.5        MsgBox(Redondear(234.62, 0.5, 0.6, 0.5)) ' devuelve 235        MsgBox(Redondear(234.50, 0.5, 0.6, 0.3)) ' devuelve 234  pero un valor de de 234.51 devolvería 234.3        MsgBox(Redondear(234.34, 0.25, 0.6, 0.3)) ' devuelve 234.3    End Sub  
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...

oscuros:
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:
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 --- dim r,s,t as string    s="Ho" : t= "la"    try        r = s & t        messagebox.show r    catch ex as exception        messagebox.show ex.exception    finally        messagebox.show " Mundo."    end try  .... 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.

Navegación

[0] Índice de Mensajes

Ir a la versión completa