2010-06-29 41 views
68

Digamos que tengo un valor de 3.4679 y quiero 3.46, ¿cómo puedo truncar con dos decimales sin redondear?Truncar Dos lugares decimales sin redondeo

He intentado el siguiente pero los tres dame 3,47:

void Main() 
{ 
    Console.Write(Math.Round(3.4679, 2,MidpointRounding.ToEven)); 
    Console.Write(Math.Round(3.4679, 2,MidpointRounding.AwayFromZero)); 
    Console.Write(Math.Round(3.4679, 2)); 
} 

Esto devuelve 3,46, pero sólo parece sucia de alguna forma:

void Main() 
{ 
    Console.Write(Math.Round(3.46799999999 -.005 , 2)); 
} 

Respuesta

95
value = Math.Truncate(100 * value)/100; 

Mira que las fracciones de este tipo no puede ser representado con precisión en punto flotante.

+9

Uso decimal para los valores y esta respuesta va a funcionar. Es poco probable que siempre funcione en cualquier representación de punto flotante. – driis

+1

Eso me hace preguntarme si debería ser posible especificar la dirección de redondeo en literales de coma flotante. Hmmmm – Steve314

+0

Tiene que haber * alguna * forma de decirle al programador que el cálculo con la suposición de que un número puede almacenar más de 308 dígitos es ** groseramente ** inapropiado. Double puede almacenar solo 15.El desbordamiento es una característica muy importante aquí, se desbordó bastante mal. –

3

¿Esto funcionaría para usted?

Console.Write(((int)(3.4679999999*100))/100.0); 
1

¿Le daría lo que quiere ((long)(3.4679 * 100))/100.0?

38

Sería más útil tener una función completa para el uso en el mundo real de truncar un decimal en C#. Esto podría ser convertido en un método de extensión decimal bastante fácil si quería:

public decimal TruncateDecimal(decimal value, int precision) 
{ 
    decimal step = (decimal)Math.Pow(10, precision); 
    decimal tmp = Math.Truncate(step * value); 
    return tmp/step; 
} 

Si necesita VB.NET intente esto:

Function TruncateDecimal(value As Decimal, precision As Integer) As Decimal 
    Dim stepper As Decimal = Math.Pow(10, precision) 
    Dim tmp As Decimal = Math.Truncate(stepper * value) 
    Return tmp/stepper 
End Function 

Entonces usarlo de esta manera:

decimal result = TruncateDecimal(0.275, 2); 

o

Dim result As Decimal = TruncateDecimal(0.275, 2) 
+1

Esto se desbordará en números grandes. – nightcoder

+0

Para agregar al codificador nocturno, el hecho de que esté usando Int32 como intermediario en su función causará desbordamientos. Debe usar Int64 si realmente debe convertirlo a un Entero. La pregunta sería por qué querría incurrir en esa sobrecarga adicional de todos modos ya que Truncate devuelve integrales decimales de todos modos. Simplemente haga algo como: paso decimal = (decimal) Math.Pow (10, precisión); return Math.Truncate (paso * valor)/paso; –

+0

Bajé el elenco a Entero. Les dejé líneas separadas para una mejor legibilidad y comprensión de cómo funciona la función. – Corgalore

20

One iss ue con los otros ejemplos es que multiplican el valor de entrada antes de dividiéndolo. Aquí hay un caso de borde que puede desbordar decimal multiplicando primero, un caso marginal, pero algo que he encontrado. Es más seguro para hacer frente a la parte fraccionaria separada de la siguiente manera:

public static decimal TruncateDecimal(this decimal value, int decimalPlaces) 
    { 
     decimal integralValue = Math.Truncate(value); 

     decimal fraction = value - integralValue; 

     decimal factor = (decimal)Math.Pow(10, decimalPlaces); 

     decimal truncatedFraction = Math.Truncate(fraction * factor)/factor; 

     decimal result = integralValue + truncatedFraction; 

     return result; 
    } 
+0

Sé que esto es viejo, pero me di cuenta y problema con esto. El factor que tiene aquí es un int y, por lo tanto, si está truncando a una gran cantidad de decimales (digamos 25), causará que el resultado final tenga un error de precisión. Lo arreglé cambiando el tipo de factor a decimal. – TheKingDave

+0

@TheKingDave: probablemente es irrelevante, pero como el factor no puede tener decimales, debería ser mejor modelarlo como largo ¿no? –

+0

@SoMoS Para mí, el decimal funcionó mejor porque me dio los valores de almacenamiento más altos para el factor. Todavía tiene una limitación pero es lo suficientemente grande para mi aplicación. Por otro lado, no pude almacenar números lo suficientemente grandes para mi aplicación. Por ejemplo, si haces un Truncado (25) con un tiempo largo, habrá cierta inexactitud. – TheKingDave

0

Si usted no se preocupe demasiado por el rendimiento y el resultado final puede ser una cadena, el siguiente enfoque será resistente a las cuestiones flotante de precisión:

string Truncate(double value, int precision) 
{ 
    if (precision < 0) 
    { 
     throw new ArgumentOutOfRangeException("Precision cannot be less than zero"); 
    } 

    string result = value.ToString(); 

    int dot = result.IndexOf('.'); 
    if (dot < 0) 
    { 
     return result; 
    } 

    int newLength = dot + precision + 1; 

    if (newLength == dot + 1) 
    { 
     newLength--; 
    } 

    if (newLength > result.Length) 
    { 
     newLength = result.Length; 
    } 

    return result.Substring(0, newLength); 
} 
+4

En realidad, hardcoding '.' no es una buena idea, mejor use System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator [0] –

14

utilizar el operador módulo:

var fourPlaces = 0.5485M; 
var twoPlaces = fourPlaces - (fourPlaces % 0.01M); 

resultado: 0,54

6

Dejaré la solución para números decimales.

Algunas de las soluciones para decimales aquí son propensas al desbordamiento (si pasamos un número decimal muy grande y el método intentará multiplicarlo).

La solución de Tim Lloyd está protegida contra el desbordamiento, pero no es demasiado rápida.

La siguiente solución es aproximadamente 2 veces más rápido y no tiene un problema de desbordamiento:

public static class DecimalExtensions 
{ 
    public static decimal TruncateEx(this decimal value, int decimalPlaces) 
    { 
     if (decimalPlaces < 0) 
      throw new ArgumentException("decimalPlaces must be greater than or equal to 0."); 

     var modifier = Convert.ToDecimal(0.5/Math.Pow(10, decimalPlaces)); 
     return Math.Round(value >= 0 ? value - modifier : value + modifier, decimalPlaces); 
    } 
} 

[Test] 
public void FastDecimalTruncateTest() 
{ 
    Assert.AreEqual(-1.12m, -1.129m. TruncateEx(2)); 
    Assert.AreEqual(-1.12m, -1.120m. TruncateEx(2)); 
    Assert.AreEqual(-1.12m, -1.125m. TruncateEx(2)); 
    Assert.AreEqual(-1.12m, -1.1255m.TruncateEx(2)); 
    Assert.AreEqual(-1.12m, -1.1254m.TruncateEx(2)); 
    Assert.AreEqual(0m,  0.0001m.TruncateEx(3)); 
    Assert.AreEqual(0m,  -0.0001m.TruncateEx(3)); 
    Assert.AreEqual(0m,  -0.0000m.TruncateEx(3)); 
    Assert.AreEqual(0m,  0.0000m.TruncateEx(3)); 
    Assert.AreEqual(1.1m, 1.12m. TruncateEx(1)); 
    Assert.AreEqual(1.1m, 1.15m. TruncateEx(1)); 
    Assert.AreEqual(1.1m, 1.19m. TruncateEx(1)); 
    Assert.AreEqual(1.1m, 1.111m. TruncateEx(1)); 
    Assert.AreEqual(1.1m, 1.199m. TruncateEx(1)); 
    Assert.AreEqual(1.2m, 1.2m. TruncateEx(1)); 
    Assert.AreEqual(0.1m, 0.14m. TruncateEx(1)); 
    Assert.AreEqual(0,  -0.05m. TruncateEx(1)); 
    Assert.AreEqual(0,  -0.049m. TruncateEx(1)); 
    Assert.AreEqual(0,  -0.051m. TruncateEx(1)); 
    Assert.AreEqual(-0.1m, -0.14m. TruncateEx(1)); 
    Assert.AreEqual(-0.1m, -0.15m. TruncateEx(1)); 
    Assert.AreEqual(-0.1m, -0.16m. TruncateEx(1)); 
    Assert.AreEqual(-0.1m, -0.19m. TruncateEx(1)); 
    Assert.AreEqual(-0.1m, -0.199m. TruncateEx(1)); 
    Assert.AreEqual(-0.1m, -0.101m. TruncateEx(1)); 
    Assert.AreEqual(0m,  -0.099m. TruncateEx(1)); 
    Assert.AreEqual(0m,  -0.001m. TruncateEx(1)); 
    Assert.AreEqual(1m,  1.99m. TruncateEx(0)); 
    Assert.AreEqual(1m,  1.01m. TruncateEx(0)); 
    Assert.AreEqual(-1m, -1.99m. TruncateEx(0)); 
    Assert.AreEqual(-1m, -1.01m. TruncateEx(0)); 
} 
+1

No me gusta el sufijo "Ex". C# admite la sobrecarga, su método 'Truncado' se agrupará junto con los nativos de .NET, proporcionando al usuario una experiencia perfecta. – Gqqnbig

+1

Su algoritmo produce algunos resultados incorrectos. El modo predeterminado MidpointRounding es Banker's Rounding, que redondea 0.5 al valor par más cercano. 'Assert.AreEqual (1.1m, 1.12m.TruncateEx (1));' falla debido a esto. Si especifica el redondeo "normal" (AwayFromZero) en la llamada Math.Round, entonces 'Assert.AreEqual (0m, 0m.TruncateEx (1));' falla –

+1

La única forma en que funcionará esta solución es si usa 'MidpointRounding. AwayFromZero' y específicamente código para manejar el valor 0. –

0

Aquí es mi implementación de la función TRUNCAR

private static object Tranc(List<Expression.Expression> p) 
{ 
    var target = (decimal)p[0].Evaluate(); 

    // check if formula contains only one argument 
    var digits = p.Count > 1 
     ? (decimal) p[1].Evaluate() 
     : 0; 

    return Math.Truncate((double)target * Math.Pow(10, (int)digits))/Math.Pow(10, (int)digits); 
} 
1

Aquí es un método de extensión:

public static decimal? TruncateDecimalPlaces(this decimal? value, int places) 
    { 
     if (value == null) 
     { 
      return null; 
     } 

     return Math.Floor((decimal)value * (decimal)Math.Pow(10, places))/(decimal)Math.Pow(10, places); 

    } // end 
-2

Actualmente quiere 3.46 de 3.4679. Esto es solo la representación de caracteres. Por lo tanto, no hay nada que ver con la función matemática. La función de matemáticas no está destinada a hacer este trabajo. Simplemente use el siguiente código.

Dim str1 As String 
str1="" 
str1 ="3.4679" 
    Dim substring As String = str1.Substring(0, 3) 

    ' Write the results to the screen. 
    Console.WriteLine("Substring: {0}", substring) 

Or 
    Please use the following code. 
Public function result(ByVal x1 As Double) As String 
    Dim i as Int32 
    i=0 
    Dim y as String 
    y = "" 
    For Each ch as Char In x1.ToString 
    If i>3 then 
    Exit For 
    Else 
    y + y +ch 
    End if 
    i=i+1 
    Next 
    return y 
End Function 

El código anterior se puede modificar para cualquier número Ponga la siguiente código en un evento de clic de botón

Dim str As String 
str= result(3.4679) 
MsgBox("The number is " & str) 
0

qué pasa con esto?

Function TruncateDecimal2(MyValue As Decimal) As Decimal 
     Try 
      Return Math.Truncate(100 * MyValue)/100 
     Catch ex As Exception 
      Return Math.Round(MyValue, 2) 
     End Try 
End Function 
0

Además de las soluciones anteriores, hay otra manera que podemos lograr.

decimal val=23.5678m,finalValue; 

    //take the decimal part  
    int decimalPos = val.ToString().IndexOf('.'); 
    string decimalPart = val.ToString().Substring(decimalPosition+1,val.ToString().Length); 
    //will result.56 
    string wholePart=val.ToString().Substring(0,decimalPos-1); 
    //concantinate and parse for decimal. 
    string truncatedValue=wholePart+decimalPart;//"23.56" 
    bool isDecimal=Decimal.tryParse(truncatedValue,out finalValue);//finalValue=23.56 
1

Universal y método rápido (sin Math.Pow()/multiplicación) para System.Decimal:

decimal Truncate(decimal d, byte decimals) 
{ 
    decimal r = Math.Round(d, decimals); 

    if (d > 0 && r > d) 
    { 
     return r - new decimal(1, 0, 0, false, decimals); 
    } 
    else if (d < 0 && r < d) 
    { 
     return r + new decimal(1, 0, 0, false, decimals); 
    } 

    return r; 
} 
0

Bajo algunas condiciones, esta puede ser suficiente.

que tenían un valor decimal de SubCent = 0.0099999999999999999999999999M que tiende a dar formato a | SubCent: 0.010000 | a través de string.Format("{0:N6}", SubCent); y muchas otras opciones de formato.

Mi requisito no era redondear el valor SubCent, pero tampoco registrar cada dígito.

A continuación se reunieron mis requisitos:

string.Format("SubCent:{0}|", 
    SubCent.ToString("N10", CultureInfo.InvariantCulture).Substring(0, 9)); 

que devuelve la cadena: | SubCent: 0.0099999 |

Para acomodar el valor que tiene una parte entera, lo siguiente es un comienzo.

tmpValFmt = 567890.0099999933999229999999M.ToString("0.0000000000000000000000000000"); 
decPt = tmpValFmt.LastIndexOf("."); 
if (decPt < 0) decPt = 0; 
valFmt4 = string.Format("{0}", tmpValFmt.Substring(0, decPt + 9)); 

que devuelve la cadena:

valFmt4 = "567890.00999999" 
Cuestiones relacionadas