Làm cách nào để định dạng độc đáo các số nổi thành Chuỗi mà không có số thập phân 0 không cần thiết?


496

Nhân đôi 64 bit có thể biểu thị chính xác số nguyên +/- 2 53

Với thực tế này, tôi chọn sử dụng loại kép làm một loại duy nhất cho tất cả các loại của mình, vì số nguyên lớn nhất của tôi không dấu 32-bit.

Nhưng bây giờ tôi phải in các số nguyên giả này, nhưng vấn đề là chúng cũng được trộn lẫn với gấp đôi thực tế.

Vậy làm thế nào để tôi in những đôi này độc đáo trong Java?

Tôi đã thử String.format("%f", value), rất gần, ngoại trừ tôi nhận được rất nhiều số 0 ở các giá trị nhỏ.

Đây là một ví dụ đầu ra của %f

232.00000000
0.18000000000
1237875192.0
4.5800000000
0,00000000
1.23450000

Điều tôi muốn là:

232
0,18
1237875192
4,58
0
1.2345

Chắc chắn tôi có thể viết một hàm để cắt các số không đó, nhưng điều đó làm giảm hiệu năng do thao tác Chuỗi. Tôi có thể làm tốt hơn với mã định dạng khác không?

BIÊN TẬP

Câu trả lời của Tom E. và Jeremy S. không thể chấp nhận được vì cả hai đều tự ý làm tròn đến 2 chữ số thập phân. Hãy hiểu vấn đề trước khi trả lời.

CHỈNH SỬA 2

Xin lưu ý rằng String.format(format, args...)miền địa phương phụ thuộc (xem câu trả lời dưới đây).


Nếu tất cả những gì bạn muốn là số nguyên, tại sao không sử dụng lâu dài? Bạn nhận được nhiều tiếng nổ hơn ở 2 ^ 63-1, không có định dạng khó xử và hiệu suất tốt hơn.
basszero

14
Bởi vì một số giá trị thực sự tăng gấp đôi
Pyrolistic

2
Một số trường hợp xảy ra sự cố này là một lỗi đã được sửa trong JDK 7: stackoverflow.com/questions/7564525/
mẹo

Có phải tôi hay JavaScript chuyển đổi chuỗi thành chuỗi tốt hơn 100% so với Java?
Andy

System.out.println("YOUR STRING" + YOUR_DOUBLE_VARIABLE);
Shaya Amani

Câu trả lời:


399

Nếu ý tưởng là in các số nguyên được lưu trữ gấp đôi như thể chúng là các số nguyên, và nếu không thì in số nhân với độ chính xác tối thiểu cần thiết:

public static String fmt(double d)
{
    if(d == (long) d)
        return String.format("%d",(long)d);
    else
        return String.format("%s",d);
}

Sản xuất:

232
0.18
1237875192
4.58
0
1.2345

Và không dựa vào thao tác chuỗi.


9
Đồng ý, đây là một câu trả lời tồi, không sử dụng nó. Nó không hoạt động với giá trị doublelớn hơn intgiá trị tối đa . Ngay cả với longnó vẫn sẽ thất bại cho số lượng lớn. Hơn nữa, nó sẽ trả về một Chuỗi ở dạng hàm mũ, ví dụ "1.0E10", cho các giá trị lớn, có lẽ không phải là điều người hỏi muốn. Sử dụng %fthay vì %strong chuỗi định dạng thứ hai để sửa lỗi đó.
jlh

26
OP tuyên bố rõ ràng rằng họ không muốn đầu ra được định dạng bằng cách sử dụng %f. Câu trả lời là cụ thể cho tình huống được mô tả và đầu ra mong muốn. OP đề xuất giá trị tối đa của chúng là int không dấu 32 bit, điều mà tôi nghĩ là có intthể chấp nhận được (không dấu không thực sự tồn tại trong Java và không có ví dụ nào có vấn đề), nhưng thay đổi intthành longmột sửa chữa nhỏ nếu tình huống khác.
JasonD

1
Trường hợp trong câu hỏi nó không nên làm điều đó?
JasonD

6
String.format("%s",d)??? Nói về chi phí không cần thiết. Sử dụng Double.toString(d). Tương tự cho cái khác : Long.toString((long)d).
Andreas

15
Vấn đề là %skhông hoạt động với Locales. Trong tiếng Đức, chúng tôi sử dụng dấu "," thay vì "." bằng số thập phân. Trong khi String.format(Locale.GERMAN, "%f", 1.5)trả về "1.500000", String.format(Locale.GERMAN, "%s", 1.5)trả về "1.5" - với dấu ".", Đó là sai trong tiếng Đức. Có phiên bản phụ thuộc địa phương của "% s" không?
Felix Edelmann

414
new DecimalFormat("#.##").format(1.199); //"1.2"

Như đã chỉ ra trong các ý kiến, đây không phải là câu trả lời đúng cho câu hỏi ban đầu.
Điều đó nói rằng, nó là một cách rất hữu ích để định dạng số mà không có các số 0 không cần thiết.


16
Một lưu ý quan trọng ở đây là 1.1 sẽ được định dạng đúng là "1.1" mà không có bất kỳ số 0 nào.
Steve Pomeroy

53
Và nếu bạn tình cờ muốn có một số lượng 0 số cụ thể (ví dụ: nếu bạn đang in số tiền) thì bạn có thể sử dụng '0' thay vì '#' (tức là DecimalFormat mới ("0,00"). Định dạng (số tiền);) không phải là những gì OP muốn, nhưng có thể hữu ích để tham khảo.
TJ Ellis

22
Vâng, như tác giả ban đầu của câu hỏi, đây là câu trả lời SAU. Buồn cười có bao nhiêu lượt bình chọn. Vấn đề với giải pháp này là nó tùy ý làm tròn đến 2 chữ số thập phân.
Pyrolistic

11
@Mazyod vì bạn luôn có thể vượt qua trong một điểm nổi với số thập phân nhiều hơn định dạng. Đó là viết mã sẽ làm việc hầu hết thời gian nhưng không bao gồm tất cả các trường hợp cạnh.
Pyrolistic

15
@Pyrolistic - IMHO, có rất nhiều upvote bởi vì, mặc dù đây là giải pháp sai cho bạn, nhưng đó là giải pháp phù hợp cho 99% + trong số những người tìm thấy Q & A này: thông thường, một vài chữ số cuối cùng của một đôi là "nhiễu", làm lộn xộn đầu ra, cản trở khả năng đọc. Do đó, lập trình viên xác định có bao nhiêu chữ số có lợi cho người đọc đầu ra và chỉ định số đó. Một tình huống phổ biến là các lỗi toán học nhỏ đã tích lũy, do đó, giá trị có thể là 12.000000034, nhưng sẽ thích làm tròn đến 12 và hiển thị nhỏ gọn là "12". Và "12.340000056" => "12.34".
ToolmakerSteve

226
String.format("%.2f", value) ;

13
Điều đó đúng nhưng luôn in các số 0 ở ngay cả khi không có phần phân số. String.format ("%. 2f, 1.0005) in 1,00 và không 1. Có bất kỳ định dạng định dạng nào để không in phần phân đoạn nếu nó không tồn tại không?
Emre Yazici

87
Đã bỏ phiếu vì câu hỏi đang yêu cầu loại bỏ tất cả các số 0 ở cuối và câu trả lời này sẽ luôn để lại hai điểm nổi bất kể bằng không.
Zulaxia

DecimalFormat là một mẹo hay - mặc dù cuối cùng tôi đã sử dụng cái này cho tình huống của mình (bộ đếm thời gian ở cấp độ trò chơi) vì các số 0 ở cuối trông tốt hơn.
Timothy Lee Russell

2
Tôi nghĩ rằng bạn có thể xử lý các số 0 chính xác bằng cách sử dụng g thay vì f.
Peter Ajtai

3
Tôi đã sử dụng giải pháp này trong một hệ thống sản xuất với "% .5f" và nó thực sự rất tệ, không sử dụng nó ... bởi vì nó đã in nó: 5.12E-4 thay vì 0,000512
hamish

87

Nói ngắn gọn:

Nếu bạn muốn thoát khỏi các vấn đề về số 0 và Locale, thì bạn nên sử dụng:

double myValue = 0.00000021d;

DecimalFormat df = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
df.setMaximumFractionDigits(340); //340 = DecimalFormat.DOUBLE_FRACTION_DIGITS

System.out.println(df.format(myValue)); //output: 0.00000021

Giải trình:

Tại sao các câu trả lời khác không phù hợp với tôi:

  • Double.toString()hoặc System.out.printlnhoặc FloatingDecimal.toJavaFormatStringsử dụng các ký hiệu khoa học nếu đôi là ít hơn 10 ^ -3 hoặc lớn hơn hoặc bằng 10 ^ 7

    double myValue = 0.00000021d;
    String.format("%s", myvalue); //output: 2.1E-7
  • bằng cách sử dụng %f, độ chính xác thập phân mặc định là 6, nếu không bạn có thể mã hóa nó nhưng nó sẽ dẫn đến các số không được thêm vào nếu bạn có số thập phân ít hơn. Thí dụ :

    double myValue = 0.00000021d;
    String.format("%.12f", myvalue); //output: 0.000000210000
  • bằng cách sử dụng setMaximumFractionDigits(0);hoặc %.0fbạn loại bỏ bất kỳ độ chính xác thập phân nào, điều này tốt cho số nguyên / độ dài nhưng không phải là gấp đôi

    double myValue = 0.00000021d;
    System.out.println(String.format("%.0f", myvalue)); //output: 0
    DecimalFormat df = new DecimalFormat("0");
    System.out.println(df.format(myValue)); //output: 0
  • bằng cách sử dụng DecimalFormat, bạn phụ thuộc cục bộ. Trong ngôn ngữ Pháp, dấu tách thập phân là dấu phẩy, không phải là điểm:

    double myValue = 0.00000021d;
    DecimalFormat df = new DecimalFormat("0");
    df.setMaximumFractionDigits(340);
    System.out.println(df.format(myvalue));//output: 0,00000021

    Sử dụng ngôn ngữ ENGLISH đảm bảo bạn có được một điểm cho dấu phân cách thập phân, bất cứ nơi nào chương trình của bạn sẽ chạy

Tại sao dùng 340 thì để setMaximumFractionDigits?

Hai lý do :

  • setMaximumFractionDigitschấp nhận một số nguyên nhưng việc thực hiện nó có một chữ số tối đa được phép DecimalFormat.DOUBLE_FRACTION_DIGITSbằng 340
  • Double.MIN_VALUE = 4.9E-324 vì vậy với 340 chữ số, bạn chắc chắn không làm tròn độ chính xác gấp đôi và lỏng lẻo của mình

Điều này không hoạt động đối với số nguyên, ví dụ "2" trở thành "2."
kap

Cảm ơn, tôi đã sửa câu trả lời bằng cách sử dụng mẫu 0thay vì#.
JBE

Bạn không sử dụng hằng số DecimalFormat.DOUBLE_FRACTION_DIGITSnhưng bạn đang sử dụng giá trị 340, sau đó bạn cung cấp một nhận xét để cho thấy rằng nó bằng DecimalFormat.DOUBLE_FRACTION_DIGITS. Tại sao không chỉ sử dụng hằng số ???
Maarten Bodewes

1
Bởi vì thuộc tính này không công khai ... nó là "thân thiện với gói"
JBE

4
Cảm ơn! Trong thực tế, câu trả lời này là câu hỏi duy nhất thực sự phù hợp với tất cả các yêu cầu được đề cập trong câu hỏi - nó không hiển thị các số không cần thiết, không làm tròn số và phụ thuộc vào địa phương. Tuyệt quá!
Felix Edelmann

26

Tại sao không:

if (d % 1.0 != 0)
    return String.format("%s", d);
else
    return String.format("%.0f",d);

Điều này sẽ hoạt động với các giá trị cực đoan được hỗ trợ bởi Double. Sản lượng:

0.12
12
12.144252
0

2
Tôi thích câu trả lời này theo đó chúng ta không cần thực hiện chuyển đổi loại.
Jeff T.

Giải thích ngắn gọn: "%s"về cơ bản các cuộc gọi d.toString()nhưng nó không hoạt động với inthoặc nếu d==null!
Neph

24

Trên máy của tôi, chức năng sau nhanh hơn khoảng 7 lần so với chức năng được cung cấp bởi câu trả lời của JasonD , vì nó tránh String.format:

public static String prettyPrint(double d) {
  int i = (int) d;
  return d == i ? String.valueOf(i) : String.valueOf(d);
}

1
Hmm, điều này không tính đến các địa điểm, nhưng JasonD cũng không.
TWiStErRob

22

2 xu của tôi:

if(n % 1 == 0) {
    return String.format(Locale.US, "%.0f", n));
} else {
    return String.format(Locale.US, "%.1f", n));
}

2
Hoặc chỉ return String.format(Locale.US, (n % 1 == 0 ? "%.0f" : "%.1f"), n);.
MC Hoàng đế

thất bại khi 23.00123 ==> 23.00
vào

11

Không, đừng bận tâm.

Mất hiệu suất do thao tác Chuỗi bằng không.

Và đây là mã để cắt kết thúc sau %f

private static String trimTrailingZeros(String number) {
    if(!number.contains(".")) {
        return number;
    }

    return number.replaceAll("\\.?0*$", "");
}

7
Tôi đánh giá thấp bởi vì giải pháp của bạn không phải là cách tốt nhất để đi. Hãy xem String.format. Bạn cần sử dụng đúng loại định dạng, nổi trong trường hợp này. Nhìn vào câu trả lời trên của tôi.
jjnguy

6
Tôi đã bỏ phiếu vì tôi đang gặp vấn đề tương tự, và dường như không ai ở đây hiểu được vấn đề.
Obay

1
Được đánh giá thấp là DecimalFormat được đề cập trong bài viết của Tom chính xác là những gì bạn đang tìm kiếm. Nó dải số không khá hiệu quả.
Steve Pomeroy

4
Về phía trên, có lẽ anh ta muốn cắt các số 0 mà không làm tròn? PS @Pyrolistic, chắc chắn bạn chỉ có thể sử dụng number.replaceAll (".? 0 * $", ""); (sau khi chứa (".") tất nhiên)
Rehno Lindeque

1
Ok, làm thế nào bạn có thể đạt được mục tiêu của tôi với DecimalFormat?
Pyrolistic

8

Sử dụng một DecimalFormatsetMinimumFractionDigits(0)


Tôi sẽ thêm setMaximumFractionDigits(2)setGroupingUsed(false)(OP không đề cập đến nó nhưng từ ví dụ có vẻ như là bắt buộc). Ngoài ra, một trường hợp thử nghiệm nhỏ không gây tổn thương vì tầm thường của nó trong trường hợp này. Tuy nhiên, vì tôi nghĩ đó là giải pháp đơn giản nhất, upvote là một upvote :)
acrespo

6
if (d == Math.floor(d)) {
    return String.format("%.0f", d);
} else {
    return Double.toString(d);
}

1
Tôi nghĩ rằng tôi sẽ theo bạn trong việc này: D
aeracode

5

Hãy lưu ý rằng String.format(format, args...)miền địa phương phụ thuộc bởi vì nó định dạng sử dụng locale mặc định của người dùng, đó là, có lẽ bằng dấu phẩy và không gian thậm chí bên trong như 123 456.789 hoặc 123,456.789 , có thể không chính xác những gì bạn mong đợi.

Bạn có thể thích sử dụng String.format((Locale)null, format, args...).

Ví dụ,

    double f = 123456.789d;
    System.out.println(String.format(Locale.FRANCE,"%f",f));
    System.out.println(String.format(Locale.GERMANY,"%f",f));
    System.out.println(String.format(Locale.US,"%f",f));

in

123456,789000
123456,789000
123456.789000

và đây là những gì sẽ String.format(format, args...)làm ở các quốc gia khác nhau.

EDIT Ok, vì đã có một cuộc thảo luận về thủ tục:

    res += stripFpZeroes(String.format((Locale) null, (nDigits!=0 ? "%."+nDigits+"f" : "%f"), value));
    ...

protected static String stripFpZeroes(String fpnumber) {
    int n = fpnumber.indexOf('.');
    if (n == -1) {
        return fpnumber;
    }
    if (n < 2) {
        n = 2;
    }
    String s = fpnumber;
    while (s.length() > n && s.endsWith("0")) {
        s = s.substring(0, s.length()-1);
    }
    return s;
}

1
bạn nên thêm nó dưới dạng một nhận xét cho câu trả lời được chấp nhận
Pyrolistic

Nhận xét không cho phép độ dài hoặc định dạng của phụ lục này. Vì nó bổ sung thông tin hữu ích, tôi nghĩ rằng nó nên được cho phép chứ không phải là xóa.
Terry Jan Reedy

5

Tôi đã thực hiện DoubleFormatterđể chuyển đổi hiệu quả một số lượng lớn các giá trị kép thành Chuỗi đẹp / có thể trình bày:

double horribleNumber = 3598945.141658554548844; 
DoubleFormatter df = new DoubleFormatter(4,6); //4 = MaxInteger, 6 = MaxDecimal
String beautyDisplay = df.format(horribleNumber);
  • Nếu phần nguyên của V có nhiều hơn MaxInteger => hiển thị V ở định dạng nhà khoa học (1.2345e + 30) nếu không thì hiển thị ở định dạng bình thường 124,45678.
  • MaxDecimal quyết định số chữ số thập phân (cắt với làm tròn số của ngân hàng)

Đây là mã:

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;

/**
 * Convert a double to a beautiful String (US-local):
 * 
 * double horribleNumber = 3598945.141658554548844; 
 * DoubleFormatter df = new DoubleFormatter(4,6);
 * String beautyDisplay = df.format(horribleNumber);
 * String beautyLabel = df.formatHtml(horribleNumber);
 * 
 * Manipulate 3 instances of NumberFormat to efficiently format a great number of double values.
 * (avoid to create an object NumberFormat each call of format()).
 * 
 * 3 instances of NumberFormat will be reused to format a value v:
 * 
 * if v < EXP_DOWN, uses nfBelow
 * if EXP_DOWN <= v <= EXP_UP, uses nfNormal
 * if EXP_UP < v, uses nfAbove
 * 
 * nfBelow, nfNormal and nfAbove will be generated base on the precision_ parameter.
 * 
 * @author: DUONG Phu-Hiep
 */
public class DoubleFormatter
{
    private static final double EXP_DOWN = 1.e-3;
    private double EXP_UP; // always = 10^maxInteger
    private int maxInteger_;
    private int maxFraction_;
    private NumberFormat nfBelow_; 
    private NumberFormat nfNormal_;
    private NumberFormat nfAbove_;

    private enum NumberFormatKind {Below, Normal, Above}

    public DoubleFormatter(int maxInteger, int maxFraction){
        setPrecision(maxInteger, maxFraction);
    }

    public void setPrecision(int maxInteger, int maxFraction){
        Preconditions.checkArgument(maxFraction>=0);
        Preconditions.checkArgument(maxInteger>0 && maxInteger<17);

        if (maxFraction == maxFraction_ && maxInteger_ == maxInteger) {
            return;
        }

        maxFraction_ = maxFraction;
        maxInteger_ = maxInteger;
        EXP_UP =  Math.pow(10, maxInteger);
        nfBelow_ = createNumberFormat(NumberFormatKind.Below);
        nfNormal_ = createNumberFormat(NumberFormatKind.Normal);
        nfAbove_ = createNumberFormat(NumberFormatKind.Above);
    }

    private NumberFormat createNumberFormat(NumberFormatKind kind) {
        final String sharpByPrecision = Strings.repeat("#", maxFraction_); //if you do not use Guava library, replace with createSharp(precision);
        NumberFormat f = NumberFormat.getInstance(Locale.US);

        //Apply banker's rounding:  this is the rounding mode that statistically minimizes cumulative error when applied repeatedly over a sequence of calculations
        f.setRoundingMode(RoundingMode.HALF_EVEN);

        if (f instanceof DecimalFormat) {
            DecimalFormat df = (DecimalFormat) f;
            DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();

            //set group separator to space instead of comma

            //dfs.setGroupingSeparator(' ');

            //set Exponent symbol to minus 'e' instead of 'E'
            if (kind == NumberFormatKind.Above) {
                dfs.setExponentSeparator("e+"); //force to display the positive sign in the exponent part
            } else {
                dfs.setExponentSeparator("e");
            }

            df.setDecimalFormatSymbols(dfs);

            //use exponent format if v is out side of [EXP_DOWN,EXP_UP]

            if (kind == NumberFormatKind.Normal) {
                if (maxFraction_ == 0) {
                    df.applyPattern("#,##0");
                } else {
                    df.applyPattern("#,##0."+sharpByPrecision);
                }
            } else {
                if (maxFraction_ == 0) {
                    df.applyPattern("0E0");
                } else {
                    df.applyPattern("0."+sharpByPrecision+"E0");
                }
            }
        }
        return f;
    } 

    public String format(double v) {
        if (Double.isNaN(v)) {
            return "-";
        }
        if (v==0) {
            return "0"; 
        }
        final double absv = Math.abs(v);

        if (absv<EXP_DOWN) {
            return nfBelow_.format(v);
        }

        if (absv>EXP_UP) {
            return nfAbove_.format(v);
        }

        return nfNormal_.format(v);
    }

    /**
     * format and higlight the important part (integer part & exponent part) 
     */
    public String formatHtml(double v) {
        if (Double.isNaN(v)) {
            return "-";
        }
        return htmlize(format(v));
    }

    /**
     * This is the base alogrithm: create a instance of NumberFormat for the value, then format it. It should
     * not be used to format a great numbers of value 
     * 
     * We will never use this methode, it is here only to understanding the Algo principal:
     * 
     * format v to string. precision_ is numbers of digits after decimal. 
     * if EXP_DOWN <= abs(v) <= EXP_UP, display the normal format: 124.45678
     * otherwise display scientist format with: 1.2345e+30 
     * 
     * pre-condition: precision >= 1
     */
    @Deprecated
    public String formatInefficient(double v) {

        final String sharpByPrecision = Strings.repeat("#", maxFraction_); //if you do not use Guava library, replace with createSharp(precision);

        final double absv = Math.abs(v);

        NumberFormat f = NumberFormat.getInstance(Locale.US);

        //Apply banker's rounding:  this is the rounding mode that statistically minimizes cumulative error when applied repeatedly over a sequence of calculations
        f.setRoundingMode(RoundingMode.HALF_EVEN);

        if (f instanceof DecimalFormat) {
            DecimalFormat df = (DecimalFormat) f;
            DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();

            //set group separator to space instead of comma

            dfs.setGroupingSeparator(' ');

            //set Exponent symbol to minus 'e' instead of 'E'

            if (absv>EXP_UP) {
                dfs.setExponentSeparator("e+"); //force to display the positive sign in the exponent part
            } else {
                dfs.setExponentSeparator("e");
            }
            df.setDecimalFormatSymbols(dfs);

            //use exponent format if v is out side of [EXP_DOWN,EXP_UP]

            if (absv<EXP_DOWN || absv>EXP_UP) {
                df.applyPattern("0."+sharpByPrecision+"E0");
            } else {
                df.applyPattern("#,##0."+sharpByPrecision);
            }
        }
        return f.format(v);
    }

    /**
     * Convert "3.1416e+12" to "<b>3</b>.1416e<b>+12</b>"
     * It is a html format of a number which highlight the integer and exponent part
     */
    private static String htmlize(String s) {
        StringBuilder resu = new StringBuilder("<b>");
        int p1 = s.indexOf('.');

        if (p1>0) {
            resu.append(s.substring(0, p1));
            resu.append("</b>");
        } else {
            p1 = 0;
        }

        int p2 = s.lastIndexOf('e');
        if (p2>0) {
            resu.append(s.substring(p1, p2));
            resu.append("<b>");
            resu.append(s.substring(p2, s.length()));
            resu.append("</b>");
        } else {
            resu.append(s.substring(p1, s.length()));
            if (p1==0){
                resu.append("</b>");
            }
        }
        return resu.toString();
    }
}

Lưu ý: Tôi đã sử dụng 2 chức năng từ thư viện GUAVA. Nếu bạn không sử dụng GUAVA, hãy tự viết mã:

/**
 * Equivalent to Strings.repeat("#", n) of the Guava library: 
 */
private static String createSharp(int n) {
    StringBuilder sb = new StringBuilder(); 
    for (int i=0;i<n;i++) {
        sb.append('#');
    }
    return sb.toString();
}

1
Nếu bạn biết độ chính xác, thì hãy sử dụng BigDecimal. Xem docs.oracle.com/javase/1.5.0/docs/api/java/math/...
Pyrolistical

5

Điều này sẽ giúp công việc được hoàn thành tốt đẹp, tôi biết chủ đề này đã cũ, nhưng tôi đã vật lộn với cùng một vấn đề cho đến khi tôi đến đây. Tôi hy vọng ai đó thấy nó hữu ích.

    public static String removeZero(double number) {
        DecimalFormat format = new DecimalFormat("#.###########");
        return format.format(number);
    }

5
new DecimalFormat("00.#").format(20.236)
//out =20.2

new DecimalFormat("00.#").format(2.236)
//out =02.2
  1. 0 cho số chữ số tối thiểu
  2. Riders # chữ số

trong khi điều này có thể cung cấp một giải pháp cho câu hỏi, cách tốt nhất là thêm một lời giải thích ngắn gọn để cộng đồng được hưởng lợi (và học hỏi) từ câu trả lời
Blurfus

4
String s = String.valueof("your int variable");
while (g.endsWith("0") && g.contains(".")) {
    g = g.substring(0, g.length() - 1);
    if (g.endsWith("."))
    {
        g = g.substring(0, g.length() - 1);
    }
}

thay vào đó, bạn chỉ nên tìm kiếm chữ số không đầu tiên từ bên phải và sau đó sử dụng chuỗi con (và cũng xác minh rằng chuỗi có chứa "." tất nhiên). bằng cách này, bạn sẽ không tạo ra quá nhiều chuỗi tạm thời trên đường.
nhà phát triển Android

3

Trả lời muộn nhưng ...

Bạn nói rằng bạn chọn lưu trữ số của bạn với loại kép . Tôi nghĩ rằng đây có thể là gốc rễ của vấn đề vì nó buộc bạn phải lưu trữ số nguyên thành gấp đôi (và do đó làm mất thông tin ban đầu về bản chất của giá trị). Điều gì về việc lưu trữ số của bạn trong các trường hợp của Số lớp (siêu lớp của cả Double và Integer) và dựa vào đa hình để xác định định dạng chính xác của mỗi số?

Tôi biết có thể không chấp nhận được để cấu trúc lại toàn bộ một phần mã của bạn do điều đó nhưng nó có thể tạo ra đầu ra mong muốn mà không cần thêm mã / truyền / phân tích cú pháp.

Thí dụ:

import java.util.ArrayList;
import java.util.List;

public class UseMixedNumbers {

    public static void main(String[] args) {
        List<Number> listNumbers = new ArrayList<Number>();

        listNumbers.add(232);
        listNumbers.add(0.18);
        listNumbers.add(1237875192);
        listNumbers.add(4.58);
        listNumbers.add(0);
        listNumbers.add(1.2345);

        for (Number number : listNumbers) {
            System.out.println(number);
        }
    }

}

Sẽ tạo ra đầu ra sau:

232
0.18
1237875192
4.58
0
1.2345

Bằng cách này, javascript đã đưa ra lựa chọn tương tự :)
Pyrolistic

@Pyrolistic Bạn có thể giải thích thêm một chút về tuyên bố của mình không? Nó không hoàn toàn rõ ràng đối với tôi ... :)
Phát hiện

2

Đây là những gì tôi nghĩ ra:

  private static String format(final double dbl) {
    return dbl % 1 != 0 ? String.valueOf(dbl) : String.valueOf((int) dbl);
  }

Đơn giản một lớp lót, chỉ phôi để int nếu thực sự cần


1
Lặp đi lặp lại những gì Felix Edelmann đã nói ở nơi khác: điều này sẽ tạo ra một chuỗi độc lập Locale, có thể không phải lúc nào cũng phù hợp với người dùng.
JJ Brown

công bằng, đối với trường hợp sử dụng của tôi, đây không phải là vấn đề, tôi không hoàn toàn chắc chắn ngay bây giờ nhưng tôi nghĩ người ta có thể sử dụng String.format (với Locale truy nã) thay vì valueOf
keisar

2

Định dạng giá với nhóm, làm tròn, không có số không cần thiết (gấp đôi).

Quy tắc:

  1. Không có số 0 ở cuối ( 2.0000 = 2; 1.0100000 = 1.01)
  2. Tối đa hai chữ số sau một điểm ( 2.010 = 2.01; 0.20 = 0.2)
  3. Làm tròn sau chữ số thứ 2 sau một điểm ( 1.994 = 1.99; 1.995 = 2; 1.006 = 1.01; 0.0006 -> 0)
  4. Trả về 0( null/-0 = 0)
  5. Thêm $( = $56/-$56)
  6. Phân nhóm ( 101101.02 = $101,101.02)

Ví dụ khác:

-99.985 = -$99.99

10 = $10

10.00 = $10

20.01000089 = $20.01

Được viết bằng Kotlin dưới dạng tiện ích mở rộng thú vị của Double (nguyên nhân được sử dụng trong Android), nhưng có thể dễ dàng chuyển đổi sang Java, vì các lớp java đã được sử dụng.

/**
 * 23.0 -> $23
 *
 * 23.1 -> $23.1
 *
 * 23.01 -> $23.01
 *
 * 23.99 -> $23.99
 *
 * 23.999 -> $24
 *
 * -0.0 -> $0
 *
 * -5.00 -> -$5
 *
 * -5.019 -> -$5.02
 */
fun Double?.formatUserAsSum(): String {
    return when {
        this == null || this == 0.0 -> "$0"
        this % 1 == 0.0 -> DecimalFormat("$#,##0;-$#,##0").format(this)
        else -> DecimalFormat("$#,##0.##;-$#,##0.##").format(this)
    }
}

Cách sử dụng:

var yourDouble: Double? = -20.00
println(yourDouble.formatUserAsSum()) // will print -$20

yourDouble = null
println(yourDouble.formatUserAsSum()) // will print $0

Giới thiệu về DecimalFormat : https://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html


1
public static String fmt(double d) {
    String val = Double.toString(d);
    String[] valArray = val.split("\\.");
    long valLong = 0;
    if(valArray.length == 2){
        valLong = Long.parseLong(valArray[1]);
    }
    if (valLong == 0)
        return String.format("%d", (long) d);
    else
        return String.format("%s", d);
}

Tôi đã phải sử dụng nguyên nhân này d == (long)dđã cho tôi vi phạm trong báo cáo sonar


1
float price = 4.30;
DecimalFormat format = new DecimalFormat("0.##"); // Choose the number of decimal places to work with in case they are different than zero and zero value will be removed
format.setRoundingMode(RoundingMode.DOWN); // choose your Rounding Mode
System.out.println(format.format(price));

đây là kết quả của một số bài kiểm tra:

4.30     => 4.3
4.39     => 4.39  // Choose format.setRoundingMode(RoundingMode.UP) to get 4.4
4.000000 => 4
4        => 4

Còn 1.23450000 thì sao?
Alex78191

1.23450000 => 1.23
Ahmed Mihoub

0

Đây là hai cách để đạt được nó. Đầu tiên, cách ngắn hơn (và có thể tốt hơn):

public static String formatFloatToString(final float f)
  {
  final int i=(int)f;
  if(f==i)
    return Integer.toString(i);
  return Float.toString(f);
  }

Và đây là cách lâu hơn và có thể tồi tệ hơn:

public static String formatFloatToString(final float f)
  {
  final String s=Float.toString(f);
  int dotPos=-1;
  for(int i=0;i<s.length();++i)
    if(s.charAt(i)=='.')
      {
      dotPos=i;
      break;
      }
  if(dotPos==-1)
    return s;
  int end=dotPos;
  for(int i=dotPos+1;i<s.length();++i)
    {
    final char c=s.charAt(i);
    if(c!='0')
      end=i+1;
    }
  final String result=s.substring(0,end);
  return result;
  }

1
đôi khi, khi bạn làm mọi thứ đơn giản hơn, mã phía sau phức tạp hơn và ít được tối ưu hóa hơn ... nhưng vâng, bạn có thể sử dụng nhiều hàm API tích hợp ...
nhà phát triển Android

1
Bạn nên bắt đầu với đơn giản và một khi bạn đã xác định bạn có vấn đề về hiệu suất, thì chỉ sau đó bạn mới nên tối ưu hóa. Mã là để con người đọc đi đọc lại. Làm cho nó chạy nhanh là thứ yếu. Bằng cách không sử dụng API tiêu chuẩn bất cứ khi nào có thể, bạn có nhiều khả năng giới thiệu các lỗi và chỉ khiến việc thay đổi trở nên khó khăn hơn trong tương lai.
Pyrolistic

3
Tôi sẽ tranh luận mã bạn viết như thế sẽ KHÔNG nhanh hơn. JVM rất thông minh và bạn thực sự không biết thứ gì nhanh hay chậm cho đến khi bạn cấu hình nó. Vấn đề về hiệu năng có thể được phát hiện và khắc phục khi nó trở thành vấn đề. Bạn không nên tối ưu hóa sớm cho nó. Viết mã cho mọi người đọc, không phải cho bạn tưởng tượng máy sẽ chạy nó như thế nào. Khi nó trở thành một vấn đề về hiệu năng, hãy viết lại mã bằng một trình lược tả.
Pyrolistic

2
Ai đó đã chỉnh sửa câu trả lời để cải thiện định dạng mã. Tôi đã xem xét vài chục chỉnh sửa để phê duyệt và sẽ phê duyệt chỉnh sửa của họ ở đây, nhưng các chỉnh sửa không nhất quán nên tôi đã sửa chúng. Tôi cũng đã cải thiện ngữ pháp của các đoạn văn bản.
Steve Vinoski

1
Tôi không hiểu Nếu bạn nói định dạng không thành vấn đề, tại sao bạn lại dành thời gian để thay đổi lại?
OrhanC1

0

Đối với Kotlin, bạn có thể sử dụng tiện ích mở rộng như:

fun Double.toPrettyString() =
    if(this - this.toLong() == 0.0)
        String.format("%d", this.toLong())
    else
        String.format("%s",this)

0

Đây là một câu trả lời khác có tùy chọn để thêm số thập phân CHỈ NẾU số thập phân không bằng không.

   /**
     * Example: (isDecimalRequired = true)
     * d = 12345
     * returns 12,345.00
     *
     * d = 12345.12345
     * returns 12,345.12
     *
     * ==================================================
     * Example: (isDecimalRequired = false)
     * d = 12345
     * returns 12,345 (notice that there's no decimal since it's zero)
     *
     * d = 12345.12345
     * returns 12,345.12
     *
     * @param d float to format
     * @param zeroCount number decimal places
     * @param isDecimalRequired true if it will put decimal even zero,
     * false will remove the last decimal(s) if zero.
     */
    fun formatDecimal(d: Float? = 0f, zeroCount: Int, isDecimalRequired: Boolean = true): String {
        val zeros = StringBuilder()

        for (i in 0 until zeroCount) {
            zeros.append("0")
        }

        var pattern = "#,##0"

        if (zeros.isNotEmpty()) {
            pattern += ".$zeros"
        }

        val numberFormat = DecimalFormat(pattern)

        var formattedNumber = if (d != null) numberFormat.format(d) else "0"

        if (!isDecimalRequired) {
            for (i in formattedNumber.length downTo formattedNumber.length - zeroCount) {
                val number = formattedNumber[i - 1]

                if (number == '0' || number == '.') {
                    formattedNumber = formattedNumber.substring(0, formattedNumber.length - 1)
                } else {
                    break
                }
            }
        }

        return formattedNumber
    }

-1

Đây là một câu trả lời thực sự hoạt động (kết hợp các câu trả lời khác nhau ở đây)

public static String removeTrailingZeros(double f)
{
    if(f == (int)f) {
        return String.format("%d", (int)f);
    }
    return String.format("%f", f).replaceAll("0*$", "");
}

1
bạn đã không thay thế ĐIỂM, ví dụ: "100.0" sẽ được chuyển đổi thành "100."
VinceStyling

if (f == (int) f) đảm nhiệm việc đó.
Martin Klosi

2
Thất bại trên f = 9999999999.00
Dawood ibn Kareem

-4

Tôi biết đây là một chủ đề thực sự cũ .. Nhưng tôi nghĩ cách tốt nhất để làm điều này là như sau:

public class Test {

    public static void main(String args[]){
        System.out.println(String.format("%s something",new Double(3.456)));
        System.out.println(String.format("%s something",new Double(3.456234523452)));
        System.out.println(String.format("%s something",new Double(3.45)));
        System.out.println(String.format("%s something",new Double(3)));
    }
}

Đầu ra:

3.456 something
3.456234523452 something
3.45 something
3.0 something

Vấn đề duy nhất là vấn đề cuối cùng trong đó .0 không bị xóa. Nhưng nếu bạn có thể sống với điều đó thì điều này hoạt động tốt nhất. % .2f sẽ làm tròn nó đến 2 chữ số thập phân cuối cùng. DecimalFormat cũng vậy. Nếu bạn cần tất cả các số thập phân nhưng không có các số 0 ở cuối thì điều này hoạt động tốt nhất.


2
DecimalFormat với định dạng "#. ##" sẽ không giữ thêm 0 nếu không cần thiết: System.out.println(new java.text.DecimalFormat("#.##").format(1.0005));sẽ in1
Aleks G

đó là quan điểm của tôi Điều gì sẽ xảy ra nếu bạn muốn 0,0005 hiển thị nếu có. Bạn sẽ làm tròn nó 2 chữ số thập phân.
sethu

OP đang hỏi làm thế nào để in các giá trị nguyên được lưu trữ thành đôi :)
Aleks G

-8
String s = "1.210000";
while (s.endsWith("0")){
    s = (s.substring(0, s.length() - 1));
}

Điều này sẽ làm cho chuỗi thả đuôi 0-s.


1
Đây là một giải pháp tốt cho câu hỏi, nếu họ chỉ quan tâm đến các số 0 bị bỏ đi, bạn sẽ thay đổi mã của mình như thế nào để cắt một dấu thập phân theo sau? tức là "1."
bakoyaro

29
Hãy cẩn thận, giải pháp của bạn sẽ chuyển đổi 1000 thành 1, đó là sai.
Aleks G
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.