불필요한 십진수 0없이 부동 숫자를 String으로 멋지게 포맷하는 방법은 무엇입니까?


496

64 비트 double은 정수 +/- 2 53을 정확하게 나타낼 수 있습니다

이 사실을 감안할 때 가장 큰 정수는 부호없는 32 비트이므로 모든 유형의 단일 유형으로 이중 유형을 사용하기로 선택했습니다.

그러나 이제 이러한 의사 정수를 인쇄해야하지만 문제는 실제 복식과 혼합되어 있다는 것입니다.

Java 에서이 복식을 어떻게 잘 인쇄합니까?

나는 String.format("%f", value)작은 값에 대해 많은 후행 0을 얻는 것을 제외하고는 가까운 시도했습니다 .

다음은 출력 예입니다. %f

232.00000000
0.18000000000
1237875192.0
4.5800000000
0.00000000
1.23450000

내가 원하는 것은 :

232
0.18
1237875192
4.58
0
1.2345

물론 제로를 트리밍하는 함수를 작성할 수는 있지만 문자열 조작으로 인해 많은 성능 손실이 발생합니다. 다른 형식 코드로 더 잘 할 수 있습니까?

편집하다

Tom E.와 Jeremy S.의 답변은 모두 소수점 이하 두 자리로 임의로 반올림되므로 받아 들일 수 없습니다. 답변하기 전에 문제를 이해하십시오.

편집 2

참고 사항 String.format(format, args...)이다 로케일 종속은 (아래 답변을 참조).


정수만 원하는 경우 long을 사용하지 않겠습니까? 2 ^ 63-1에서 더 강렬하고 어색한 형식이 없으며 성능이 향상되었습니다.
basszero

14
일부 값은 실제로 두 배이므로
Pyrolistical

2
이 문제가 발생한 일부 사례는 JDK 7에서 수정 된 버그였습니다. stackoverflow.com/questions/7564525/…
Pyrolistical

Java보다 숫자에서 문자열로 변환 할 때 JavaScript가 100 % 더 좋습니까?
Andy

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

답변:


399

아이디어가 정수인 것처럼 double로 저장된 정수를 인쇄하는 경우 필요한 최소 정밀도로 double을 인쇄하십시오.

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

생산 :

232
0.18
1237875192
4.58
0
1.2345

그리고 문자열 조작에 의존하지 않습니다.


9
동의, 이것은 나쁜 대답입니다, 그것을 사용하지 마십시오. double최대 int값 보다 큰 값 으로 작동하지 않습니다 . 그럼에도 불구하고 long여전히 큰 숫자는 실패합니다. 또한 큰 값의 경우 지수 형식으로 문자열을 반환합니다 (예 : "1.0E10"). 어쩌면 원하는 사람이 아닐 수도 있습니다. 이를 해결하려면 두 번째 형식 문자열 %f대신 사용하십시오 %s.
jlh

26
OP는 명시 적으로를 사용하여 출력 형식을 원하지 않는다고 명시했습니다 %f. 답변은 설명 된 상황과 원하는 결과에 따라 다릅니다. 영업 이익은 최대 값이 나는 것을 의미했다 32 비트 부호없는 int를 제안했다 int허용했다 (실제로 자바에 존재하지 않는 부호없는, 어떤 모범이 문제가 없었다)하지만, 변화 int하는 것은 long상황이 다른 경우 사소한 수정입니다.
JasonD

1
문제의 어느 부분에서 그렇게해서는 안된다고 말합니까?
JasonD

6
String.format("%s",d)??? 불필요한 오버 헤드에 대해 이야기하십시오. 사용하십시오 Double.toString(d). 다른 사람도 마찬가지입니다 Long.toString((long)d).
Andreas

15
문제는 %s로케일에서 작동하지 않는다는 것입니다. 독일어에서는 "."대신 ","를 사용합니다. 십진수로. String.format(Locale.GERMAN, "%f", 1.5)"1,500000"을 String.format(Locale.GERMAN, "%s", 1.5)반환 하는 동안 "1.5"를 반환합니다. "."은 독일어에서는 false입니다. "% s"의 로케일 종속 버전도 있습니까?
Felix Edelmann

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

의견에서 지적했듯이 이것은 원래 질문에 대한 정답이 아닙니다.
즉, 불필요한 후행 0없이 숫자를 형식화하는 매우 유용한 방법입니다.


16
여기서 중요한 점은 1.1은 후행 0없이 "1.1"으로 올바르게 형식화된다는 것입니다.
Steve Pomeroy

53
그리고 특정 수의 후행 0을 원한다면 (예를 들어 돈을 인쇄하는 경우) '#'대신 '0'을 사용할 수 있습니다 (예 : 새로운 DecimalFormat ( "0.00"). format (amount);) OP가 원하는 것이 아니지만 참조에 유용 할 수 있습니다.
TJ Ellis

22
그렇습니다. 질문의 원저자로서 이것은 잘못된 답변입니다. 얼마나 많은 투표가 있는지 재미 있습니다. 이 솔루션의 문제점은 임의로 소수점 이하 2 자리로 반올림한다는 것입니다.
Pyrolistical

11
@Mazyod는 항상 형식보다 소수점이 많은 부동 소수점을 전달할 수 있기 때문입니다. 그것은 대부분의 시간 동안 작동하지만 모든 중요한 경우를 다루지는 않는 코드를 작성하는 것입니다.
Pyrolistical

15
@Pyrolistical-IMHO, 이것은 당신에게 잘못된 해결책이지만,이 Q & A를 찾는 사람들의 99 % +에 대한 올바른 해결책이기 때문에 많은 공감대가 있습니다 : 일반적으로, 더블의 마지막 몇 자리는 "잡음", 가독성을 방해하는 출력을 어수선하게 만듭니다. 따라서 프로그래머는 출력을 읽는 사람에게 유익한 자릿수를 결정하고 그 숫자를 지정합니다. 일반적인 상황은 작은 수학 오류가 누적 된 것이므로 값은 12.000000034 일 수 있지만 12로 반올림하고 "12"로 컴팩트하게 표시하는 것을 선호합니다. 그리고 "12.340000056"=> "12.34".
ToolmakerSteve

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

13
정확하지만 소수 부분이 없더라도 항상 후행 0을 인쇄합니다. String.format ( "%. 2f, 1.0005)는 1.00이 아닌 1을 인쇄합니다. 소수 부분이 존재하지 않으면이를 인쇄하지 않는 형식 지정자가 있습니까?
Emre Yazici

87
질문이 모든 후행 0을 제거하도록 요청한 이후 투표에 실패 했으며이 답변은 항상 0이 아닌 두 개의 부동 소수점을 남깁니다.
Zulaxia

DecimalFormat은 좋은 트릭이었습니다. 후행하는 0이 더 좋을 때 내 상황 (게임 레벨 타이머)에 사용했습니다.
Timothy Lee Russell

2
f 대신 g를 사용하여 후행 0을 올바르게 처리 할 수 ​​있다고 생각합니다.
피터 Ajtai

3
나는 "%의 .5f"와 생산 시스템에서이 솔루션을 사용하고,이 인쇄 때문에 ... 그것을 사용하지 않는 정말 정말 나쁜 : 5.12E-4 대신 0.000512
해미시

87

한마디로 :

후행 0과 로케일 문제를 제거하려면 다음을 사용해야합니다.

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

설명:

다른 답변이 나에게 적합하지 않은 이유 :

  • Double.toString()또는 System.out.println또는 FloatingDecimal.toJavaFormatString이중 10 개 미만인 경우 ^ -3 과학적 표기법을 사용하거나보다 큰 10 ^ 7 동등

    double myValue = 0.00000021d;
    String.format("%s", myvalue); //output: 2.1E-7
  • 을 사용 %f하면 기본 10 진수 정밀도는 6이며, 그렇지 않으면 하드 코딩 할 수 있지만 소수가 적 으면 추가 0이 추가됩니다. 예 :

    double myValue = 0.00000021d;
    String.format("%.12f", myvalue); //output: 0.000000210000
  • 십진 정밀도 를 사용 setMaximumFractionDigits(0);하거나 %.0f제거 하여 정수 / 길이에는 좋지만 두 배에는 적합하지 않습니다.

    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
  • DecimalFormat을 사용하면 로컬에 따라 다릅니다. 프랑스어 로케일에서 소수점 구분 기호는 점이 아닌 쉼표입니다.

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

    ENGLISH 로케일을 사용하면 프로그램이 실행되는 곳에서 소수점 구분 기호를 얻을 수 있습니다

왜 340을 사용 setMaximumFractionDigits합니까?

두 가지 이유 :

  • setMaximumFractionDigits정수를 허용하지만 구현에 허용되는 최대 자릿수는 DecimalFormat.DOUBLE_FRACTION_DIGITS340입니다.
  • Double.MIN_VALUE = 4.9E-324 따라서 340 자리 숫자로 배정도의 정밀도를 반올림하지 않아야합니다.

정수에는 작동하지 않습니다. 예를 들어 "2"는 "2"가됩니다.
kap

감사합니다, 나는 패턴을 사용하여 답을 수정 한 0대신#.
JBE

상수를 사용하지 않고 DecimalFormat.DOUBLE_FRACTION_DIGITS값 340을 사용하고 있습니다.이 값은이라는 값을 나타내는 주석을 제공합니다 DecimalFormat.DOUBLE_FRACTION_DIGITS. 왜 상수를 사용하지 않습니까 ???
Maarten Bodewes

1
이 속성은 공용이 아니기 때문에 "패키지 친화적"입니다.
JBE

4
감사! 실제로이 답변은 질문에 언급 된 모든 요구 사항과 실제로 일치하는 유일한 답변입니다. 불필요한 0을 표시하지 않고 숫자를 반올림하지 않으며 로케일에 따라 다릅니다. 큰!
Felix Edelmann

26

왜 안되 겠어요 :

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

이것은 Double에서 지원하는 극단적 인 값에서 작동합니다. 수율 :

0.12
12
12.144252
0

2
형식 변환을 수행 할 필요가없는이 답변을 선호합니다.
Jeff T.

짧은 설명 : "%s"기본적으로 호출 d.toString()하지만 작동하지 int않거나 if d==null!
Neph

24

내 컴퓨터에서 다음 기능은 JasonD의 답변 에서 제공하는 기능보다 약 7 배 빠릅니다 String.format.

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

1
흠, 이것은 로케일을 고려하지 않지만 JasonD도 고려하지 않습니다.
TWiStErRob

22

내 2 센트 :

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

2
아니면 그냥 return String.format(Locale.US, (n % 1 == 0 ? "%.0f" : "%.1f"), n);.
MC 황제

23.00123 ==> 23.00 일 때 실패
aswzen

11

아냐, 신경 쓰지 마

문자열 조작으로 인한 성능 손실은 0입니다.

그리고 끝을 다듬는 코드는 다음과 같습니다. %f

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

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

7
귀하의 솔루션이 최선의 방법이 아니기 때문에 하향 투표했습니다. String.format을 살펴보십시오. 이 경우 float 형식을 사용해야합니다. 위의 답변을보십시오.
jjnguy 2009

6
나는 같은 문제가 있기 때문에 투표를했는데 여기에 아무도 문제를 이해하지 못하는 것 같습니다.
Obay

1
Tom의 게시물에 언급 된 DecimalFormat이 정확히 당신이 찾고있는 것입니다. 0을 매우 효과적으로 제거합니다.
Steve Pomeroy

4
위와 같이, 그는 반올림없이 0을 트리밍하고 싶습니까? PS @Pyrolistical, 당신은 반드시 number.replaceAll ( ".? 0 * $", ""); (물론 contains ( ".") 이후)
Rehno Lindeque

1
그렇다면 DecimalFormat으로 내 목표를 어떻게 달성 할 수 있습니까?
Pyrolistical

8

사용하십시오 DecimalFormatsetMinimumFractionDigits(0)


나는 추가 setMaximumFractionDigits(2)하고 setGroupingUsed(false)(OP는 언급하지 않지만 예를 들어 필요한 것으로 보인다). 또한 작은 테스트 케이스는이 경우 사소한 것이므로 아프지 않습니다. 아직도, 그것이 가장 간단한 해결책이라고 생각하기 때문에
공감은 공감입니다

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

1
나는 이것에서 당신을 따를 것이라고 생각합니다 : D
aeracode

5

참고 사항 String.format(format, args...)이다 로케일 종속 이 포맷 때문에 사용자의 기본 로케일을 사용하여 아마 같은 내부 쉼표, 심지어 공백, (123) 456789 또는 123,456.789 정확히 당신이 무엇을 기대 할 수있다.

을 사용하는 것이 좋습니다 String.format((Locale)null, format, args...).

예를 들어

    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));

인쇄물

123456,789000
123456,789000
123456.789000

그리고 이것은 String.format(format, args...)다른 나라에서 할 것 입니다.

형식에 대한 논의가 있었기 때문에 편집하십시오.

    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
허용 된 답변에 주석으로 이것을 추가해야합니다
Pyrolistical

주석은이 부록의 길이나 형식을 허용하지 않습니다. 유용한 정보를 추가 할 수 있기 때문에 삭제하기보다는 그대로 허용해야한다고 생각합니다.
Terry Jan Reedy

5

난을 만들어 DoubleFormatter효율적으로 좋은 / 흉 String으로 두 배 값의 큰 숫자를 변환 :

double horribleNumber = 3598945.141658554548844; 
DoubleFormatter df = new DoubleFormatter(4,6); //4 = MaxInteger, 6 = MaxDecimal
String beautyDisplay = df.format(horribleNumber);
  • V의 정수 부분이 MaxInteger =>보다 큰 경우 과학자 형식으로 V를 표시하면 (1.2345e + 30) 그렇지 않으면 일반 형식으로 124.45678로 표시됩니다.
  • MaxDecimal은 소수 자릿수를 결정합니다 (은행가의 반올림으로 자르기)

코드는 다음과 같습니다.

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();
    }
}

참고 : GUAVA 라이브러리의 2 가지 기능을 사용했습니다. GUAVA를 사용하지 않는 경우 직접 코딩하십시오.

/**
 * 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
정밀도를 알고 있으면 BigDecimal을 사용하십시오. docs.oracle.com/javase/1.5.0/docs/api/java/math/…
Pyrolistical

5

이것으로 작업을 훌륭하게 수행 할 수 있습니다. 주제가 오래되었다는 것을 알고 있지만이 문제가 발생하기 전에는 같은 문제로 고심하고있었습니다. 누군가가 유용하다고 생각합니다.

    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
  2. # 자리 렌더링

이것은 질문에 대한 해결책을 제공 할 수 있지만, 공동체가 답변으로부터 이익을 얻고 배우기 위해 간단한 설명을 추가하는 것이 좋습니다
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);
    }
}

대신 오른쪽에서 0이 아닌 첫 번째 숫자를 검색 한 다음 subString을 사용하십시오 (또한 문자열에 "."가 포함되어 있는지 확인). 이런 식으로, 당신은 도중에 너무 많은 임시 문자열을 만들지 않을 것입니다.
안드로이드 개발자

3

늦게 대답했지만 ...

당신은 당신 이 더블 타입으로 숫자를 저장하기로 결정 했다 . 이것이 문제의 근본 원인이라고 생각합니다. 정수 를 두 배로 저장해야 하므로 값의 특성에 대한 초기 정보가 손실되기 때문입니다. 무엇의 경우에 귀하의 번호를 저장에 대한 클래스 (Double 및 Integer의 수퍼 클래스) 하고 다형성을 사용하여 각 숫자의 올바른 형식을 결정하는 것은 어떻습니까?

그로 인해 코드의 전체 부분을 리팩터링하는 것이 허용되지 않을 수 있지만 추가 코드 / 캐스팅 / 파싱없이 원하는 출력을 생성 할 수 있습니다.

예:

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);
        }
    }

}

다음과 같은 출력을 생성합니다.

232
0.18
1237875192
4.58
0
1.2345


@Pyrolistical 좀 더 당신의 진술을 설명 할 수 있습니까? 그것은 아주 :) ... 나를 위해 분명하지 않다
발견

2

이것이 내가 생각해 낸 것입니다.

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

간단한 하나의 라이너, 실제로 필요한 경우에만 int로 캐스트


1
Felix Edelmann이 다른 곳에서 말한 내용을 반복하면 로케일 독립적 문자열이 생성되어 사용자에게 항상 적합한 것은 아닙니다.
JJ Brown

공정한 포인트, 내 유스 케이스의 경우 이것이 문제가 아니 었으므로 지금은 확실하지 않지만 valueOf 대신 String.format (원하는 로케일 포함)을 사용할 수 있다고 생각합니다.
keisar

2

그룹화, 반올림, 불필요한 0을 두 배로 사용하여 가격 형식을 지정하십시오.

규칙 :

  1. 끝에 0이 없습니다 ( 2.0000 = 2; 1.0100000 = 1.01)
  2. 포인트 뒤 최대 두 자리수 ( 2.010 = 2.01; 0.20 = 0.2)
  3. 점 다음에 두 번째 자리 이후 반올림 ( 1.994 = 1.99; 1.995 = 2; 1.006 = 1.01; 0.0006 -> 0)
  4. 반품 0( null/-0 = 0)
  5. 추가 $(= $56/-$56 )
  6. 그룹화 ( 101101.02 = $101,101.02)

더 많은 예 :

-99.985 = -$99.99

10 = $10

10.00 = $10

20.01000089 = $20.01

Kotlin에서 Double의 재미있는 확장으로 작성되었지만 (Android에서 사용됨) Java로 쉽게 변환 할 수 있으므로 Java 클래스가 사용되었습니다.

/**
 * 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)
    }
}

사용하는 방법:

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

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

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);
}

d == (long)d수중 음파 탐지기 보고서 에서이 원인 을 위반해야했습니다.


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));

이것은 몇 가지 테스트의 결과입니다.

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

1.23450000은 어떻습니까?
Alex78191

1.23450000 => 1.23
Ahmed Mihoub

0

이를 달성하는 두 가지 방법이 있습니다. 첫째, 더 짧고 아마도 더 좋은 방법입니다.

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

그리고 더 길고 아마도 더 나쁜 방법이 있습니다.

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
때때로, 당신이 일을 더 단순하게 만들 때, 코드 뒤의 코드는 더 복잡하고 덜 최적화되어 있습니다. 그러나 그렇습니다. 많은 내장 API 함수를 사용할 수 있습니다 ...
android developer

1
간단하게 시작해야하며 일단 성능 문제가 있다고 판단되면 최적화해야합니다. 코드는 인간이 계속해서 읽고 읽는 것입니다. 빠르게 실행하는 것이 보조입니다. 가능할 때마다 표준 API를 사용하지 않으면 버그가 발생할 가능성이 높아지고 앞으로 변경하기가 더 어려워집니다.
Pyrolistical

3
나는 당신이 작성한 코드가 더 빠르지 않을 것이라고 주장합니다. JVM은 매우 영리하며 실제로 프로파일 링 할 때까지 속도가 얼마나 느리거나 느린 지 알 수 없습니다. 성능 문제가 발생하면이를 감지하고 수정할 수 있습니다. 조기에 최적화하지 않아야합니다. 사람들이 읽을 수있는 코드를 작성하십시오. 성능 문제가되면 프로파일 러로 코드를 다시 작성하십시오.
Pyrolistical

2
다른 사람이 코드 형식을 개선하기 위해 답변을 편집했습니다. 승인을 위해 수십 가지 수정 사항을 검토하고 수정 사항을 여기에서 승인하려고했지만 수정 사항이 일치하지 않아 수정했습니다. 또한 텍스트 스 니펫의 문법을 개선했습니다.
Steve Vinoski

1
이해가 안 돼요 서식이 중요하지 않다고 말하면 왜 다시 형식을 바꾸는 데 시간을 보냈습니까?
OrhanC1 2016 년

0

Kotlin의 경우 다음과 같은 확장을 사용할 수 있습니다.

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

0

다음은 10 진수가 0이 아닌 경우 에만 10 진수를 추가하는 옵션이있는 또 다른 대답입니다 .

   /**
     * 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

실제로 작동하는 답변은 다음과 같습니다 (여기에서 다른 답변의 조합).

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

1
예를 들어, "100.0"은 "100"으로 변환됩니다.
VinceStyling

if (f == (int) f)가 처리합니다.
Martin Klosi 2013

2
f = 9999999999.00에 실패
Dawood ibn Kareem

-4

나는 이것이 정말로 오래된 스레드라는 것을 알고있다. 그러나 나는 이것을하는 가장 좋은 방법은 다음과 같다고 생각한다.

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)));
    }
}

산출:

3.456 something
3.456234523452 something
3.45 something
3.0 something

유일한 문제는 .0이 제거되지 않은 마지막 문제입니다. 그러나 당신이 그걸로 살 수 있다면 이것은 가장 잘 작동합니다. % .2f는 마지막 2 자리 10 진수로 반올림합니다. DecimalFormat도 마찬가지입니다. 소수점 이하 자릿수는 모두 필요하지만 후행 0은 필요하지 않습니다.


2
"#. ##"형식의 DecimalFormat은 필요하지 않은 경우 추가 0을 유지하지 않습니다. System.out.println(new java.text.DecimalFormat("#.##").format(1.0005));인쇄1
Aleks G

그게 내 요점입니다. 0.0005가있는 경우 표시하려면 어떻게합니까? 소수점 이하 두 자리를 반올림합니다.
sethu

OP는 double에 저장된 정수 값을 인쇄하는 방법을 묻고 있습니다 :)
Aleks G

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

이렇게하면 문자열이 테일링 0을 삭제합니다.


1
이것은 후행 0을 삭제하는 데 관심이 있다면 질문에 대한 좋은 해결책입니다. 후행 소수점을 자르도록 코드를 어떻게 변경 하시겠습니까? 즉 "1"
bakoyaro

29
솔루션이 1000을 1로 변환하면 잘못됩니다.
Aleks G
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.