Eliminar ceros finales

177

Tengo algunos campos devueltos por una colección como

2.4200
2.0044
2.0000

Quiero resultados como

2.42
2.0044
2

Intenté con String.Format, pero vuelve 2.0000y configurándolo para N0redondear los otros valores también.

Zo Has
fuente
3
inicialmente tipo de registro es una cadena ??
Singleton
2
Vea mi respuesta: String.Format()con 'G' debería obtener lo que desea ... He actualizado mi respuesta con un enlace a formatos numéricos estándar. Muy útil
Dog Ears
3
Se puede convertir un decimal doubley el valor predeterminado ToStringpara doble emite los ceros finales. lea esto
Shimmy Weitzhandler
2
Y probablemente costará menos rendimiento (interms de gran cantidad de registros) que pasar la "G" como formato de cadena a la ToStringfunción.
Shimmy Weitzhandler
44
No debe convertir un decimal a un doble, perderá importancia y puede introducir el poder de dos imprecisiones de redondeo.
kristianp

Respuestas:

167

¿No es tan simple como esto, si la entrada ES una cadena? Puedes usar uno de estos:

string.Format("{0:G29}", decimal.Parse("2.0044"))

decimal.Parse("2.0044").ToString("G29")

2.0m.ToString("G29")

Esto debería funcionar para todas las entradas.

Actualización Consulte los formatos numéricos estándar . He tenido que establecer explícitamente el especificador de precisión en 29, ya que los documentos indican claramente:

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

Actualización Konrad señaló en los comentarios:

Tenga cuidado con valores como 0.000001. El formato G29 los presentará de la forma más corta posible, por lo que cambiará a la notación exponencial. string.Format("{0:G29}", decimal.Parse("0.00000001",System.Globalization.CultureInfo.GetCultureInfo("en-US")))dará "1E-08" como resultado.

Orejas de perro
fuente
1
De todas las respuestas, ha tenido los menos pasos involucrados. Gracias Dog Ears.
Zo tiene
44
var d = 2.0m; d.ToString ("G");
Dave Hillier el
3
@Dave Hillier - Ya veo, he corregido mi respuesta. Salud. [añadido explícito 'especificador de precisión']
Dog Ears
16
Tenga cuidado con valores como 0.000001. El formato G29 los presentará de la forma más corta posible, por lo que cambiará a la notación exponencial. string.Format ("{0: G29}", decimal.Parse ("0.00000001", System.Globalization.CultureInfo.GetCultureInfo ("en-US"))) dará "1E-08" como resultado
Konrad
15
@Konrad: ¿hay alguna forma de evitar la notación científica para los números que tienen 5 o 6 decimales?
Jill
202

Me encontré con el mismo problema, pero en un caso en el que no tengo control de la salida a la cadena, que fue atendida por una biblioteca. Después de buscar detalles en la implementación del tipo Decimal (ver http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx ), se me ocurrió un buen truco (aquí como una extensión método):

public static decimal Normalize(this decimal value)
{
    return value/1.000000000000000000000000000000000m;
}

La parte exponente del decimal se reduce a lo que se necesita. Llamar a ToString () en el decimal de salida escribirá el número sin ningún 0 final. Por ejemplo

1.200m.Normalize().ToString();
Thomas Materna
fuente
29
Esta respuesta es propia porque, a diferencia de cualquier otra respuesta sobre esta pregunta (e incluso todo el tema) es realmente FUNCIONA y hace lo que el OP está pidiendo.
Coxy
2
¿es el número de 0s una cantidad exacta aquí o solo para cubrir los valores más esperados?
Simon_Weaver
3
MSDN declara "El factor de escala es implícitamente el número 10, elevado a un exponente que varía de 0 a 28", que entiendo como el número decimal tendrá como máximo 28 dígitos más allá del punto decimal. Cualquier número de ceros> = 28 debería funcionar.
Thomas Materna
2
¡Esta es la mejor respuesta! El número en la respuesta tiene 34 cifras, 1seguidas por 33 0-s, pero eso crea exactamente la misma decimalinstancia que un número con 29 cifras, una 1y 28 0-s. Justo como dijo @ThomasMaterna en su comentario. No System.Decimalpuede tener más de 29 cifras (los números con dígitos iniciales mayores que 79228...solo pueden tener 28 cifras en total) Si desea más ceros al final, multiplique por en 1.000...mlugar de dividir. Además, Math.Roundpuede cortar algunos ceros, por ejemplo , Math.Round(27.40000m, 2)da 27.40m, por lo que solo queda un cero.
Jeppe Stig Nielsen
2
Gran truco, pero NO funciona en Mono y no está garantizado para funcionar en versiones de funciones de .NET
Bigjim
87

En mi opinión, es más seguro usar cadenas de formato numérico personalizado .

decimal d = 0.00000000000010000000000m;
string custom = d.ToString("0.#########################");
// gives: 0,0000000000001
string general = d.ToString("G29");
// gives: 1E-13
Michał Powaga
fuente
25
+1 para la única respuesta que se basa en un comportamiento estable y documentado.
sinelaw
3
Debe poner 28 caracteres '#', consulte blackwasp.co.uk/DecimalTrailingZeroes.aspx .
Jaime Hablutzel
32

Uso este código para evitar la notación científica "G29":

public static string DecimalToString(this decimal dec)
{
    string strdec = dec.ToString(CultureInfo.InvariantCulture);
    return strdec.Contains(".") ? strdec.TrimEnd('0').TrimEnd('.') : strdec;
}

EDITAR: utilizando el sistema CultureInfo.NumberFormat.NumberDecimalSeparator:

public static string DecimalToString(this decimal dec)
{
    string sep = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
    string strdec = dec.ToString(CultureInfo.CurrentCulture);
    return strdec.Contains(sep) ? strdec.TrimEnd('0').TrimEnd(sep.ToCharArray()) : strdec;
}
x7BiT
fuente
1
Esta es una respuesta maravillosa. Es muy simple y puedes ver exactamente lo que está sucediendo en lugar de usar un formateador de cuerdas mágicas.
Timothy Gonzalez
9
Agradable, pero no funcionará para las culturas que usan comas para el separador decimal. Tendrías que usarCulture.NumberFormat.NumberDecimalSeparator
blearyeye
1
El mejor enfoque Todo lo demás es innecesariamente complicado. Solo recuerde siempre verificar si su número contiene un punto decimal.
RRM
1
Para ser una extensión, falta el "esto" antes del parámetro: cadena estática pública DecimalToString (este decimal dec)
João Antunes
Muchas gracias por los comentarios. He actualizado la respuesta con una nueva versión con los consejos.
x7BiT
19

Use el #símbolo hash ( ) para mostrar solo los ceros finales cuando sea necesario. Ver las pruebas a continuación.

decimal num1 = 13.1534545765;
decimal num2 = 49.100145;
decimal num3 = 30.000235;

num1.ToString("0.##");       //13.15%
num2.ToString("0.##");       //49.1%
num3.ToString("0.##");       //30%
Fizzix
fuente
77
No es una respuesta No está eliminando los ceros finales, está eliminando la precisión. El método propuesto num1.ToString("0.##")debería devolver 13.1534545765(sin cambios) porque no hay ceros finales.
Jan 'splite' K.
Y es exactamente por eso que estoy mostrando las respuestas a mis tres entradas propuestas para que los usuarios puedan ver cómo funciona mi solución.
Fizzix
No responde la pregunta incluso si enumera las entradas propuestas.
Dave Friedel
2
Lo votaría 10 veces si pudiera. ¡Exactamente lo que necesitaba!
SubqueryCrunch
1
Respuesta perfecta a la pregunta. La mejor respuesta en mi opinión. El comentario de que elimina la precisión es correcto, pero esa no era la pregunta. Lo más importante: es lo que necesito. Mis usuarios suelen ingresar números enteros, tal vez algo así como 2.5, pero necesito admitir 3 dígitos más allá del punto decimal. Así que quiero darles lo que ingresaron, no con un montón de ceros que no ingresaron. por ejemplo, Console.Write ($ "num3 = {num3: 0. ##}"); => num3 = 30
bobwki
2

Un enfoque de muy bajo nivel, pero creo que esta sería la forma más eficaz utilizando solo cálculos enteros rápidos (y sin métodos lentos de análisis de cadenas y sensibles a la cultura):

public static decimal Normalize(this decimal d)
{
    int[] bits = decimal.GetBits(d);

    int sign = bits[3] & (1 << 31);
    int exp = (bits[3] >> 16) & 0x1f;

    uint a = (uint)bits[2]; // Top bits
    uint b = (uint)bits[1]; // Middle bits
    uint c = (uint)bits[0]; // Bottom bits

    while (exp > 0 && ((a % 5) * 6 + (b % 5) * 6 + c) % 10 == 0)
    {
        uint r;
        a = DivideBy10((uint)0, a, out r);
        b = DivideBy10(r, b, out r);
        c = DivideBy10(r, c, out r);
        exp--;
    }

    bits[0] = (int)c;
    bits[1] = (int)b;
    bits[2] = (int)a;
    bits[3] = (exp << 16) | sign;
    return new decimal(bits);
}

private static uint DivideBy10(uint highBits, uint lowBits, out uint remainder)
{
    ulong total = highBits;
    total <<= 32;
    total = total | (ulong)lowBits;

    remainder = (uint)(total % 10L);
    return (uint)(total / 10L);
}
Bigjim
fuente
1
Intenté seguir esta ruta, pero la optimicé aún más, por lo que se volvió mucho más rápido que el tuyo, por ejemplo, al no dividir las entradas altas y medias (tu ay b) en cada iteración mientras todos son cero de todos modos. Sin embargo, encontré esta solución sorprendentemente simple que también supera fácilmente lo que tú y yo hemos logrado con respecto al rendimiento .
Eugene Beresovsky
2

Esto funcionará:

decimal source = 2.4200m;
string output = ((double)source).ToString();

O si su valor inicial es string:

string source = "2.4200";
string output = double.Parse(source).ToString();

Presta atención a este comentario .

Shimmy Weitzhandler
fuente
2
@Damien, sí, y tenga en cuenta que para estos propósitos (no sentirá nada a menos que haga mil millones de registros), primero porque el doble es más rápido, segundo, porque pasar un formato de cadena a la función ToString cuesta más rendimiento que no pasando params. de nuevo, no sentirá nada a menos que trabaje en miles de millones de registros.
Shimmy Weitzhandler
1
No tiene que especificar G, porque double.ToStringelimina los ceros finales de forma predeterminada.
Shimmy Weitzhandler
44
Tenga cuidado con valores como 0.000001. Estos se presentarán en la notación exponencial. double.Parse ("0.00000001", System.Globalization.CultureInfo.GetCultureInfo ("en-US")). ToString () dará "1E-08" como resultado
Konrad
17
NUNCA hagas esto. Por lo general, la razón por la que tiene un decimal es porque está representando un número con precisión (no aproximadamente como lo hace un doble). De acuerdo, este error de coma flotante es probablemente bastante pequeño, pero podría dar lugar a mostrar números incorrectos, no obstante
Adam Tegen el
2
¡Ay, convertir un tipo de datos de 128 bits (decimal) en un tipo de datos de 64 bits (doble) para formatear, no es una buena idea!
avl_sweden
2

Esto es simple.

decimal decNumber = Convert.ToDecimal(value);
        return decNumber.ToString("0.####");

Probado

Salud :)

Rakesh
fuente
1

Depende de lo que represente su número y de cómo desee administrar los valores: ¿es una moneda, necesita redondeo o truncamiento, necesita este redondeo solo para mostrar?

Si para visualización, considere formatear los números son x.ToString ("")

http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx y

http://msdn.microsoft.com/en-us/library/0c899ak8.aspx

Si solo se trata de redondeo, use Math.Round overload que requiere una sobrecarga MidPointRounding

http://msdn.microsoft.com/en-us/library/ms131274.aspx )

Si obtiene su valor de una base de datos, considere la conversión en lugar de la conversión: valor doble = (decimal) myRecord ["columnName"];

florín
fuente
0

Solo puede establecer como:

decimal decNumber = 23.45600000m;
Console.WriteLine(decNumber.ToString("0.##"));
Danilo Ferreira
fuente
1
Para que eso funcione, debe poner veintiocho caracteres '#' en lugar de solo dos.
Jaime Hablutzel
sí, entendí mal su pregunta ... = /
Danilo Ferreira
0

En caso de que desee mantener el número decimal, intente el siguiente ejemplo:

number = Math.Floor(number * 100000000) / 100000000;
phuongnd
fuente
0

Intentando hacer una solución más amigable de DecimalToString ( https://stackoverflow.com/a/34486763/3852139 ):

private static decimal Trim(this decimal value)
{
    var s = value.ToString(CultureInfo.InvariantCulture);
    return s.Contains(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator)
        ? Decimal.Parse(s.TrimEnd('0'), CultureInfo.InvariantCulture)
        : value;
}

private static decimal? Trim(this decimal? value)
{
    return value.HasValue ? (decimal?) value.Value.Trim() : null;
}

private static void Main(string[] args)
{
    Console.WriteLine("=>{0}", 1.0000m.Trim());
    Console.WriteLine("=>{0}", 1.000000023000m.Trim());
    Console.WriteLine("=>{0}", ((decimal?) 1.000000023000m).Trim());
    Console.WriteLine("=>{0}", ((decimal?) null).Trim());
}

Salida:

=>1
=>1.000000023
=>1.000000023
=>
Dmitri
fuente
0

Realicé los siguientes métodos de extensión para mí para adaptarme 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
-1

La respuesta muy simple es usar TrimEnd (). Aquí está el resultado,

double value = 1.00;
string output = value.ToString().TrimEnd('0');

La salida es 1 Si mi valor es 1.01, entonces mi salida será 1.01

Raj De Inno
fuente
44
¿Qué tal si value = 100?
Stephen Drew
44
@Raj De Inno Incluso si el valor es 1.00, la salida que está dando es "1." no "1"
Simba
-1

El siguiente código podría usarse para no usar el tipo de cadena:

int decimalResult = 789.500
while (decimalResult>0 && decimalResult % 10 == 0)
{
    decimalResult = decimalResult / 10;
}
return decimalResult;

Devuelve 789.5

Raul Minon
fuente
-2

Truncar los ceros finales es muy fácil, resuelva con una conversión dúplex:

        decimal mydecimal = decimal.Parse("1,45000000"); //(I)
        decimal truncate = (decimal)(double)mydecimal;   //(II)

(I) -> Analiza el valor decimal de cualquier fuente de cadena.

(II) -> Primero: Cast para duplicar esto, eliminar los ceros finales. Segundo: Otro conversión a decimal porque no existe conversión implícita de decimal a doble y viceversa)

amedriveroperez
fuente
55
Asume todos los valores que cabrían en un decimal, caben en un doble. Esto puede causar una Excepción de desbordamiento. También puede perder precisión.
Martin Mulder
-2

prueba este código:

string value = "100";
value = value.Contains(".") ? value.TrimStart('0').TrimEnd('0').TrimEnd('.') : value.TrimStart('0');
Raju
fuente
¿Por qué dos respuestas, mi amigo?
Raging Bull
2
¿Qué pasa con las configuraciones regionales que no usan '.'?
Martin Mulder
no es una opción en comparación con otros enfoques dentro de esta publicación. Convertir en cadena y viceversa siempre es una mala opción para manipular números (al menos debido a la configuración regional)
Vladimir Semashkin
-11

intenta así

string s = "2.4200";

s = s.TrimStart('0').TrimEnd('0', '.');

y luego convertir eso para flotar

único
fuente
1
puede usar el subString()método para eso, pero de acuerdo con la pregunta publicada aquí, no veo ningún requisito como ese =)
Singleton
2
¿Qué pasa con las configuraciones regionales que no usan '.'
Dave Hillier el
10
Esto falla miserablemente para los números que son un múltiplo par de 10.0.
Ben Voigt
1
Lo que dijo @BenVoigt es completamente correcto, ya "12300.00"que se recortará un examen "123". Otro efecto que parece indeseable es que algunos números, como los que "0.00"se recortan hasta la cadena vacía ""(aunque este número es un caso especial de ser un "múltiplo de 10.0" integral).
Jeppe Stig Nielsen