Hapus nol tertinggal

177

Saya memiliki beberapa bidang dikembalikan oleh koleksi sebagai

2.4200
2.0044
2.0000

Saya ingin hasil seperti

2.42
2.0044
2

Saya mencoba String.Format, tetapi mengembalikan 2.0000dan mengaturnya untuk N0membulatkan nilai-nilai lain juga.

Zo Has
sumber
3
awalnya jenis rekaman adalah string ??
Singleton
2
Lihat jawaban saya: String.Format()dengan 'G' harus mendapatkan yang Anda inginkan .. saya sudah memperbarui jawaban saya dengan tautan ke format angka standar. Sangat userful.
Dog Ears
3
Desimal dapat dilemparkan ke double, dan default ToStringuntuk double memancarkan nol trailing. baca ini
Shimmy Weitzhandler
2
Dan mungkin akan lebih murah biayanya (interms dari jumlah rekaman yang sangat besar) daripada melewatkan "G" sebagai string-format ke ToStringfungsi.
Shimmy Weitzhandler
4
Anda tidak boleh mengonversi desimal menjadi ganda, itu akan kehilangan signifikansi, dan dapat memperkenalkan kekuatan dua ketidakakuratan pembulatan.
kristianp

Jawaban:

167

Apakah tidak sesederhana ini, jika inputnya berupa string? Anda dapat menggunakan salah satu dari ini:

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

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

2.0m.ToString("G29")

Ini harus bekerja untuk semua input.

Perbarui Lihat Format Numerik Standar yang saya miliki untuk secara eksplisit mengatur specifier presisi ke 29 sebagai dokumen jelas menyatakan:

Namun, jika angka tersebut adalah Desimal dan specifier presisi dihilangkan, notasi titik tetap selalu digunakan dan nol trailing dipertahankan.

Pembaruan Konrad ditunjukkan dalam komentar:

Perhatikan nilai-nilai seperti 0,000001. Format G29 akan menyajikannya dengan cara sesingkat mungkin sehingga akan beralih ke notasi eksponensial. string.Format("{0:G29}", decimal.Parse("0.00000001",System.Globalization.CultureInfo.GetCultureInfo("en-US")))akan memberikan "1E-08" sebagai hasilnya.

Telinga Anjing
sumber
1
Dari semua jawaban, langkah Anda paling sedikit. Telinga Anjing terima kasih.
Zo Memiliki
4
var d = 2.0m; d.ToString ("G");
Dave Hillier
3
@ Dave Hillier - Begitu ya, saya sudah mengoreksi jawaban saya. Bersulang. [ditambahkan 'penentu presisi' eksplisit]
Dog Ears
16
Perhatikan nilai-nilai seperti 0,000001. Format G29 akan menyajikannya dengan cara sesingkat mungkin sehingga akan beralih ke notasi eksponensial. string.Format ("{0: G29}", desimal.Parse ("0,00000001", System.Globalization.CultureInfo.GetCultureInfo ("en-US"))) akan memberikan "1E-08" sebagai hasilnya
Konrad
15
@ Konrad - adakah cara untuk menghindari notasi ilmiah untuk angka yang memiliki 5 atau 6 tempat desimal?
Jill
202

Saya mengalami masalah yang sama tetapi dalam kasus di mana saya tidak memiliki kontrol output ke string, yang dirawat oleh perpustakaan. Setelah melihat detail penerapan tipe Desimal (lihat http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx ), saya menemukan trik yang rapi (di sini sebagai ekstensi metode):

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

Bagian eksponen desimal dikurangi menjadi apa yang dibutuhkan. Memanggil ToString () pada desimal output akan menulis nomor tanpa tertinggal 0. Misalnya

1.200m.Normalize().ToString();
Thomas Materna
sumber
29
Jawaban ini dimiliki karena tidak seperti pada dasarnya setiap jawaban lain pada pertanyaan ini (dan bahkan seluruh subjek) sebenarnya BEKERJA dan melakukan apa yang diminta OP.
Coxy
2
Apakah jumlah 0s jumlah yang tepat di sini atau hanya untuk menutupi nilai yang paling diharapkan?
Simon_Weaver
3
MSDN menyatakan "Faktor penskalaan secara implisit adalah angka 10, dinaikkan ke eksponen mulai dari 0 hingga 28", yang saya pahami karena angka desimal akan memiliki paling banyak 28 digit melewati titik desimal. Sejumlah nol> = 28 harus berfungsi.
Thomas Materna
2
Ini jawaban terbaik! Angka dalam jawaban memiliki 34 angka, 1diikuti oleh 33-an 0, tetapi itu menciptakan decimalcontoh yang sama persis dengan angka dengan 29 angka, satu 1dan 28 0-s. Sama seperti @ThomasMaterna katakan dalam komentarnya. Tidak System.Decimaldapat memiliki lebih dari 29 angka (angka dengan digit terdepan lebih besar dari yang 79228...bisa dimiliki hanya 28 angka). Jika Anda ingin lebih banyak membuntuti nol, gandakan dengan 1.000...malih-alih membaginya. Juga, Math.Rounddapat memotong beberapa nol, misalnya Math.Round(27.40000m, 2)memberi 27.40m, jadi hanya satu nol yang tersisa.
Jeppe Stig Nielsen
2
Trik hebat tetapi TIDAK bekerja pada Mono dan tidak dijamin untuk bekerja pada versi fitur .NET
Bigjim
87

Menurut pendapat saya lebih aman menggunakan Custom Numeric Format String .

decimal d = 0.00000000000010000000000m;
string custom = d.ToString("0.#########################");
// gives: 0,0000000000001
string general = d.ToString("G29");
// gives: 1E-13
Michał Powaga
sumber
25
+1 untuk satu-satunya jawaban yang bergantung pada perilaku stabil dan terdokumentasi.
sinelaw
3
Anda harus meletakkan 28 karakter di sana, lihat blackwasp.co.uk/DecimalTrailingZeroes.aspx .
Jaime Hablutzel
32

Saya menggunakan kode ini untuk menghindari notasi ilmiah "G29":

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

EDIT: using system 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
sumber
1
Ini jawaban yang bagus. Ini sangat sederhana dan Anda dapat melihat apa yang terjadi daripada menggunakan formatter string ajaib.
Timothy Gonzalez
9
Bagus, tetapi itu tidak akan berfungsi untuk budaya yang menggunakan koma untuk pemisah desimal. Anda harus menggunakanCulture.NumberFormat.NumberDecimalSeparator
blearyeye
1
Pendekatan terbaik. Segala sesuatu yang lain tidak perlu rumit. Ingatlah selalu untuk memeriksa apakah nomor Anda mengandung titik desimal.
RRM
1
Agar ekstensi tidak memiliki "ini" sebelum parameter: public static string DecimalToString (desimal desimal ini)
João Antunes
Terima kasih banyak atas komentarnya. Saya telah memperbarui jawabannya dengan versi baru dengan tipsnya.
x7BiT
19

Gunakan #simbol hash ( ) untuk hanya menampilkan trailing 0 bila perlu. Lihat tes di bawah ini.

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
sumber
7
Bukan jawaban. Anda tidak menghapus nol trailing, Anda menghapus presisi. Metode yang Anda usulkan num1.ToString("0.##")harus mengembalikan 13.1534545765(tidak ada perubahan) karena tidak ada nol tambahan.
Jan 'splite' K.
Dan itulah mengapa saya menampilkan jawaban untuk tiga input yang diusulkan agar pengguna dapat melihat cara kerja solusi saya.
Fizzix
Itu tidak menjawab pertanyaan bahkan jika Anda mencantumkan input yang diusulkan.
Dave Friedel
2
Id upvote ini 10 kali jika saya bisa. Persis apa yang saya butuhkan!
SubqueryCrunch
1
Jawaban sempurna untuk pertanyaan itu. Jawaban TERBAIK menurut saya. Komentar bahwa itu menghilangkan presisi adalah benar, tetapi itu bukan pertanyaannya. Paling penting - itulah yang saya butuhkan. Pengguna saya biasanya akan memasukkan bilangan bulat, mungkin sekitar 2,5, tetapi saya harus mendukung 3 digit di luar angka desimal. Jadi saya ingin memberi mereka apa yang mereka masukkan, bukan dengan sekelompok nol mereka tidak masuk. mis., Console.Write ($ "num3 = {num3: 0. ##}"); => num3 = 30
bobwki
2

Pendekatan level yang sangat rendah, tapi saya yakin ini akan menjadi cara yang paling performan dengan hanya menggunakan perhitungan integer cepat (dan tidak ada metode penguraian string lambat dan kultur sensitif):

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
sumber
1
Saya telah mencoba untuk pergi rute ini, tetapi dioptimalkan lebih lanjut sehingga jadi benar-benar jauh lebih cepat daripada Anda, misalnya dengan tidak membagi hi dan mid ints (Anda adan b) di setiap iterasi sementara jika mereka semua nol pula. Namun kemudian saya menemukan solusi sederhana yang mengejutkan ini yang juga dengan mudah mengalahkan apa yang Anda dan saya hasilkan dengan bijaksana .
Eugene Beresovsky
2

Ini akan berhasil:

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

Atau jika nilai awal Anda adalah string:

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

Perhatikan komentar ini .

Shimmy Weitzhandler
sumber
2
@ Damen, ya, dan perhatikan, bahwa untuk puroposis ini (Anda tidak akan merasakan apa-apa kecuali Anda melakukan satu miliar rekaman), pertama karena ganda lebih cepat, kedua, karena meneruskan format string ke fungsi ToString memerlukan kinerja lebih banyak daripada tidak melewati params. lagi, Anda tidak akan merasakan apa-apa kecuali Anda bekerja pada gazillions catatan.
Shimmy Weitzhandler
1
Anda tidak harus menentukan G, double.ToStringkarena secara default menghapus nol jejak.
Shimmy Weitzhandler
4
Perhatikan nilai-nilai seperti 0,000001. Ini akan disajikan dalam notasi eksponensial. double.Parse ("0,00000001", System.Globalization.CultureInfo.GetCultureInfo ("en-US")). ToString () akan menghasilkan "1E-08" sebagai hasilnya
Konrad
17
Jangan pernah melakukan ini. Biasanya, alasan Anda memiliki desimal adalah karena Anda merepresentasikan angka dengan tepat (tidak kira-kira sama dengan ganda). Memang, kesalahan floating point ini mungkin cukup kecil, tetapi dapat mengakibatkan menampilkan angka yang salah, tidak ada yang kurang
Adam Tegen
2
Aduh, mengonversi datatype 128-bit (desimal) menjadi datatype 64-bit (dobel) untuk format, bukan ide yang baik!
avl_sweden
2

Ini sederhana.

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

Diuji.

Bersulang :)

Rakesh
sumber
1

Bergantung pada apa yang angka Anda wakili dan bagaimana Anda ingin mengelola nilai: apakah ini mata uang, apakah Anda perlu pembulatan atau pemotongan, apakah Anda perlu pembulatan ini hanya untuk tampilan?

Jika untuk tampilan pertimbangkan memformat angka-angka tersebut x.ToString ("")

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

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

Jika hanya pembulatan, gunakan kelebihan beban Math.Round yang membutuhkan kelebihan MidPointRounding

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

Jika Anda mendapatkan nilai dari basis data, pertimbangkan untuk menggunakan casting alih-alih konversi: double value = (desimal) myRecord ["columnName"];

florin
sumber
0

Anda bisa menetapkan sebagai:

decimal decNumber = 23.45600000m;
Console.WriteLine(decNumber.ToString("0.##"));
Danilo Ferreira
sumber
1
Agar itu berhasil, Anda harus meletakkan dua puluh delapan '#' karakter bukan hanya dua.
Jaime Hablutzel
ya, saya salah mengerti pertanyaannya ... = /
Danilo Ferreira
0

Jika Anda ingin menyimpan angka desimal, coba contoh berikut:

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

Mencoba melakukan solusi DecimalToString yang lebih ramah ( 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());
}

Keluaran:

=>1
=>1.000000023
=>1.000000023
=>
Dmitri
sumber
-1

Jawaban yang sangat sederhana adalah dengan menggunakan TrimEnd (). Inilah hasilnya,

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

Output adalah 1 Jika nilai saya adalah 1,01 maka output saya akan menjadi 1,01

Raj De Inno
sumber
4
Bagaimana kalau value = 100?
Stephen Drew
4
@Raj De Inno Bahkan jika nilainya 1,00 output yang diberikannya adalah "1." bukan "1"
Simba
-1

Kode berikut dapat digunakan untuk tidak menggunakan tipe string:

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

Pengembalian 789.5

Raul Minon
sumber
-2

Truncate trailing Zero sangat mudah, atasi dengan duplex cast:

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

(I) -> Parse nilai desimal dari sembarang sumber string.

(II) -> Pertama: Cast untuk menggandakan ini menghapus nol trailing. Kedua: Lainnya dilemparkan ke desimal karena tidak ada konversi implisit dari desimal ke ganda dan sebaliknya

amedriveroperez
sumber
5
Anda menganggap semua nilai yang cocok dengan desimal, cocok dengan ganda. Ini dapat menyebabkan OverflowExeception. Anda juga bisa kehilangan presisi.
Martin Mulder
-2

coba kode ini:

string value = "100";
value = value.Contains(".") ? value.TrimStart('0').TrimEnd('0').TrimEnd('.') : value.TrimStart('0');
Raju
sumber
Kenapa dua jawaban, temanku?
Raging Bull
2
Bagaimana dengan lokal yang tidak menggunakan '.'?
Martin Mulder
sama sekali bukan pilihan dibandingkan dengan pendekatan lain dalam posting ini. Mengubah ke string dan kembali selalu merupakan opsi yang buruk untuk memanipulasi angka (setidaknya karena lokal)
Vladimir Semashkin
-11

coba seperti ini

string s = "2.4200";

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

dan kemudian mengubahnya menjadi mengambang

Singleton
sumber
1
Anda dapat menggunakan subString()metode untuk itu, tetapi menurut pertanyaan yang diposting di sini, saya tidak melihat persyaratan seperti itu =)
Singleton
2
Bagaimana dengan lokal yang tidak menggunakan '.'
Dave Hillier
10
Ini gagal total untuk angka yang merupakan kelipatan 10.0.
Ben Voigt
1
Apa yang dikatakan @BenVoigt sepenuhnya benar, karena sebuah ujian "12300.00"akan dipangkas "123". Efek lain yang tampaknya tidak diinginkan adalah bahwa beberapa angka, seperti "0.00"dipangkas habis hingga string kosong ""(meskipun angka ini adalah kasus khusus sebagai integral "kelipatan 10,0").
Jeppe Stig Nielsen