La mejor manera de mostrar decimales sin ceros finales

107

¿Existe un formateador de pantalla que genere decimales como estas representaciones de cadena en c # sin hacer ningún redondeo?

// decimal -> string

20 -> 20
20.00 -> 20
20.5 -> 20.5
20.5000 -> 20.5
20.125 -> 20.125
20.12500 -> 20.125
0.000 -> 0

{0. #} se redondeará y el uso de alguna función de tipo de recorte no funcionará con una columna numérica enlazada en una cuadrícula.

JC.
fuente

Respuestas:

147

¿Tiene un número máximo de lugares decimales que necesitará mostrar? (Tus ejemplos tienen un máximo de 5).

Si es así, creo que formatear con "0. #####" haría lo que quieras.

    static void Main(string[] args)
    {
        var dList = new decimal[] { 20, 20.00m, 20.5m, 20.5000m, 20.125m, 20.12500m, 0.000m };

        foreach (var d in dList)
            Console.WriteLine(d.ToString("0.#####"));
    }
Toby
fuente
4
Y siempre puede arrojar una cantidad exorbitante * de símbolos # al formato. * no científico
Anthony Pegram
3
+1: dicho sea de paso, no necesita el # inicial. "0. #####" funciona de manera idéntica. Y de cualquier manera, esto se redondea desde cero (solo para su información).
TrueWill
14
@TrueWill En realidad, marca la diferencia. d == 0.1m luego "0. #" representa "0.1" y "#. #" representa ".1" sin el 0 inicial. Ninguno de los dos es incorrecto o correcto, solo depende de lo que desee.
AaronLS
4
Necesitaba mostrar un lugar decimal en todo momento, así que usé la cadena de formato "0.0#".
Códigos con Hammer
1
Vea la respuesta de Erwin Mayer a continuación.
Shlgug
32

Acabo de aprender a utilizar correctamente el Gespecificador de formato. Consulte la documentación de MSDN . Hay una nota un poco más abajo que indica que los ceros finales se conservarán para los tipos decimales cuando no se especifique precisión. No sé por qué harían esto, pero especificar el número máximo de dígitos para nuestra precisión debería solucionar ese problema. Entonces, para formatear decimales, G29es la mejor opción.

decimal test = 20.5000m;
test.ToString("G"); // outputs 20.5000 like the documentation says it should
test.ToString("G29"); // outputs 20.5 which is exactly what we want
Schmalls
fuente
21
Sin embargo
vea el
17

Este formato de cadena debería hacer su día: "0. ###########################". Sin embargo, tenga en cuenta que los decimales pueden tener como máximo 29 dígitos significativos.

Ejemplos:

? (1000000.00000000000050000000000m).ToString("0.#############################")
-> 1000000.0000000000005

? (1000000.00000000000050000000001m).ToString("0.#############################")
-> 1000000.0000000000005

? (1000000.0000000000005000000001m).ToString("0.#############################")
-> 1000000.0000000000005000000001

? (9223372036854775807.0000000001m).ToString("0.#############################")
-> 9223372036854775807

? (9223372036854775807.000000001m).ToString("0.#############################")
-> 9223372036854775807.000000001
Erwin Mayer
fuente
4
Ésta es la respuesta más general al problema. No estoy seguro de por qué este no es el formato "G" predeterminado para decimal. Los ceros finales no agregan información. La documentación de Microsoft tiene una advertencia extraña para el decimal: docs.microsoft.com/en-us/dotnet/standard/base-types/… Sin embargo, si el número es un decimal y se omite el especificador de precisión, siempre se usa la notación de punto fijo y se conservan los ceros finales.
Eric Roller
10

Esta es otra variación de lo que vi arriba. En mi caso, necesito conservar todos los dígitos significativos a la derecha del punto decimal, lo que significa eliminar todos los ceros después del dígito más significativo. Solo pensé que sería bueno compartirlo. Sin embargo, no puedo dar fe de la eficiencia de esto, pero cuando intentas lograr la estética, ya estás condenado a las ineficiencias.

public static string ToTrimmedString(this decimal target)
{
    string strValue = target.ToString(); //Get the stock string

    //If there is a decimal point present
    if (strValue.Contains("."))
    {
        //Remove all trailing zeros
        strValue = strValue.TrimEnd('0');

        //If all we are left with is a decimal point
        if (strValue.EndsWith(".")) //then remove it
            strValue = strValue.TrimEnd('.');
    }

    return strValue;
}

Eso es todo, solo quería tirar mis dos centavos.

dislexicanaboko
fuente
3
Esta es fácilmente la mejor solución, tampoco se dispara por "0.0". Agregué una bandera predeterminada para que los enteros se muestren opcionalmente como "1.0", y esto acaba de hacer que todos mis decimales se alineen. No puedo entender por qué esta no es la mejor respuesta, + muchos.
Steve Hibbert
2
Esto es especialmente bueno si alguien ya te ha pasado una cadena. Puede simplemente cambiarlo a ToTrimmedString (esta cadena strValue) y eliminar la primera línea.
PRMan
Ese es un buen punto. Creo que cuando estaba escribiendo esto estaba tratando con valores que ya eran decimales o algo así, así que quería abarcar todo. En su lugar, podría tener el cuerpo de ese método dentro de otro método sobrecargado que acepte una cadena.
dyslexicanaboko
Es una buena solución cuando puede llamar a ToTrimmedString () pero hay muchos casos en los que está limitado a una cadena de formato.
Mike Marynowski
1
Si bien el separador decimal debe ser específico de la cultura, creo que esta es una buena solución. Una pequeña pregunta, ¿por qué no solo en strValue.TrimEnd('0').TrimEnd('.')lugar del EndsWithcheque?
nawfal
5

Otra solución, basada en la respuesta de dyslexicanaboko , pero independiente de la cultura actual:

public static string ToTrimmedString(this decimal num)
{
    string str = num.ToString();
    string decimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
    if (str.Contains(decimalSeparator))
    {
        str = str.TrimEnd('0');
        if(str.EndsWith(decimalSeparator))
        {
            str = str.RemoveFromEnd(1);
        }
    }
    return str;
}

public static string RemoveFromEnd(this string str, int characterCount)
{
    return str.Remove(str.Length - characterCount, characterCount);
}
David Dostal
fuente
Excelente. A excepción de SqlDecimal, es CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator a pesar de la configuración regional.
user2091150
4

Método de extensión:

public static class Extensions
{
    public static string TrimDouble(this string temp)
    {
        var value = temp.IndexOf('.') == -1 ? temp : temp.TrimEnd('.', '0');
        return value == string.Empty ? "0" : value;
    }
}

Código de ejemplo:

double[] dvalues = {20, 20.00, 20.5, 20.5000, 20.125, 20.125000, 0.000};
foreach (var value in dvalues)
    Console.WriteLine(string.Format("{0} --> {1}", value, value.ToString().TrimDouble()));

Console.WriteLine("==================");

string[] svalues = {"20", "20.00", "20.5", "20.5000", "20.125", "20.125000", "0.000"};
foreach (var value in svalues)
    Console.WriteLine(string.Format("{0} --> {1}", value, value.TrimDouble()));

Salida:

20 --> 20
20 --> 20
20,5 --> 20,5
20,5 --> 20,5
20,125 --> 20,125
20,125 --> 20,125
0 --> 0
==================
20 --> 20
20.00 --> 2
20.5 --> 20.5
20.5000 --> 20.5
20.125 --> 20.125
20.125000 --> 20.125
0.000 --> 0
jgauffin
fuente
1
"20.00 -> 2" Creo que no es un buen comportamiento. Para solucionarlo, llame a TrimEnd dos veces: TrimEnd ('0'); TrimEnd (',');
Vadim
2
NO USE el código escrito arriba. @VadymK tiene razón en que su comportamiento es defectuoso. Recortará los ceros finales que están ANTES del punto si no hay ningún punto.
Sevin7
2
Las respuestas en stackoverflow no son para copiar y pegar. Se utilizan como fuente de aprendizaje . Este código de muestra le enseña a usar IndexOfpara buscar caracteres dentro de una cadena. Es bastante fácil ajustar el ejemplo para resolver el problema sin decimales.
jgauffin
2

No creo que sea posible de inmediato, pero un método simple como este debería hacerlo

public static string TrimDecimal(decimal value)
{
    string result = value.ToString(System.Globalization.CultureInfo.InvariantCulture);
    if (result.IndexOf('.') == -1)
        return result;

    return result.TrimEnd('0', '.');
}
Tim Skauge
fuente
2

Es bastante fácil de hacer de inmediato:

Decimal YourValue; //just as example   
String YourString = YourValue.ToString().TrimEnd('0','.');

que eliminará todos los ceros finales de su decimal.

Lo único que debe hacer es agregar .ToString().TrimEnd('0','.');a una variable decimal para convertir a Decimalen a Stringsin ceros finales, como en el ejemplo anterior.

En algunas regiones debería ser un .ToString().TrimEnd('0',',');(donde usan una coma en lugar de un punto, pero también puede agregar un punto y una coma como parámetros para estar seguro).

(también puede agregar ambos como parámetros)

Mervin
fuente
Puede que no se dé cuenta de esto, pero cuando se trata de un paramsargumento, no es necesario que construya una matriz explícitamente. Entonces, en lugar de lo detallado TrimEnd("0".ToCharArray()), podría simplemente escribir TrimEnd('0')(nota: pasó solo en charlugar de a char[]).
Dan Tao
@Dan Tao: Gracias, lo olvidé. Ha pasado un tiempo desde que usé C #. Aunque ambas versiones funcionan, he editado mi publicación.
Mervin
@Mervin: debe pasar a char, no a string(por lo tanto: comillas simples, no comillas dobles). Lo arreglé para ti, espero que no te importe.
Dan Tao
1
Su solución producirá un resultado como "2". cuando solo tiene ceros.
jgauffin
2
Entonces sugeriría llamar a .ToString (). TrimEnd ('0'). TrimEnd ('.'). También en lugar de '.' es mejor tener CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator para que funcione en todas las culturas.
Ε Г И І И О
1
decimal val = 0.000000000100m;
string result = val == 0 ? "0" : val.ToString().TrimEnd('0').TrimEnd('.');
Jerry Liang
fuente
0

Terminé con el siguiente código:

    public static string DropTrailingZeros(string test)
    {
        if (test.Contains(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator))
        {
            test = test.TrimEnd('0');
        }

        if (test.EndsWith(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator))
        {
            test = test.Substring(0,
                test.Length - CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator.Length);
        }

        return test;
    }
Arsen Zahray
fuente
0

Terminé con esta variante:

public static string Decimal2StringCompact(decimal value, int maxDigits)
    {
        if (maxDigits < 0) maxDigits = 0;
        else if (maxDigits > 28) maxDigits = 28;
        return Math.Round(value, maxDigits, MidpointRounding.ToEven).ToString("0.############################", CultureInfo.InvariantCulture);
    }

Ventajas:

puede especificar el número máximo de dígitos significativos después del punto para mostrar en tiempo de ejecución;

puede especificar explícitamente un método redondo;

puedes controlar explícitamente una cultura.

lilo0
fuente
0

Puedes crear un método de extensión

public static class ExtensionMethod {
  public static decimal simplify_decimal(this decimal value) => decimal.Parse($"{this:0.############}");
}
KevinBui
fuente
0

Hice los siguientes métodos de extensión para que se ajustaran a uno de mis proyectos, pero tal vez sean beneficiosos para otra persona.

using System.Numerics;
using System.Text.RegularExpressions;
internal static class ExtensionMethod
{
    internal static string TrimDecimal(this BigInteger obj) => obj.ToString().TrimDecimal();
    internal static string TrimDecimal(this decimal obj) => new BigInteger(obj).ToString().TrimDecimal();
    internal static string TrimDecimal(this double obj) => new BigInteger(obj).ToString().TrimDecimal();
    internal static string TrimDecimal(this float obj) => new BigInteger(obj).ToString().TrimDecimal();
    internal static string TrimDecimal(this string obj)
    {
        if (string.IsNullOrWhiteSpace(obj) || !Regex.IsMatch(obj, @"^(\d+([.]\d*)?|[.]\d*)$")) return string.Empty;
        Regex regex = new Regex("^[0]*(?<pre>([0-9]+)?)(?<post>([.][0-9]*)?)$");
        MatchEvaluator matchEvaluator = m => string.Concat(m.Groups["pre"].Length > 0 ? m.Groups["pre"].Value : "0", m.Groups["post"].Value.TrimEnd(new[] { '.', '0' }));
        return regex.Replace(obj, matchEvaluator);
    }
}

Aunque requerirá una referencia a System.Numerics.

Jeremy McCrorie
fuente