كيفية تنسيق الأرقام العائمة بشكل جيد على السلسلة دون العلامة العشرية غير الضرورية 0؟

StackOverflow https://stackoverflow.com/questions/703396

سؤال

يمكن أن يمثل العدد المزدوج 64 بت عددًا صحيحًا +/- 253 بالضبط

نظرًا لهذه الحقيقة، اخترت استخدام نوع مزدوج كنوع واحد لجميع أنواعي، نظرًا لأن أكبر عدد صحيح لدي هو 32 بت غير موقع.

لكن الآن يجب علي طباعة هذه الأعداد الصحيحة الزائفة، لكن المشكلة هي أنها مختلطة أيضًا مع الأعداد المضاعفة الفعلية.

فكيف يمكنني طباعة هذه الزوجي بشكل جيد في جافا؟

لقد حاولت String.format("%f", value), ، وهو قريب، إلا أنني أحصل على الكثير من الأصفار الزائدة للقيم الصغيرة.

فيما يلي مثال لإخراج of %f

232.00000000
0.18000000000
1237875192.0
4.5800000000
0.00000000
1.23450000

ما أريده هو:

232
0.18
1237875192
4.58
0
1.2345

بالتأكيد يمكنني كتابة دالة لتقليص تلك الأصفار، ولكن هذا يمثل خسارة كبيرة في الأداء بسبب التلاعب بالسلسلة.هل يمكنني القيام بعمل أفضل باستخدام رمز تنسيق آخر؟

يحرر

إجابات توم إي.و جيريمي س.غير مقبولة حيث يتم تقريبهما بشكل تعسفي إلى منزلتين عشريتين.الرجاء فهم المشكلة قبل الرد.

تحرير 2

يرجى ملاحظة ذلك String.format(format, args...) يكون تعتمد على اللغة (انظر الإجابات أدناه).

هل كانت مفيدة؟

المحلول

إذا كانت الفكرة هي طباعة الأعداد الصحيحة المخزنة كأعداد صحيحة كما لو كانت أعدادًا صحيحة، وبخلاف ذلك طباعة الأعداد الزوجية بالحد الأدنى من الدقة اللازمة:

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

ولا يعتمد على التلاعب بالسلسلة.

نصائح أخرى

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

كما أشير في التعليقات، هذه ليست الإجابة الصحيحة على السؤال الأصلي.
ومع ذلك، فهي طريقة مفيدة جدًا لتنسيق الأرقام بدون أصفار زائدة غير ضرورية.

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

باختصار:

إذا كنت تريد التخلص من الأصفار اللاحقة ومشكلات الإعدادات المحلية، فيجب عليك استخدام:

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, ، الدقة العشرية الافتراضية هي 6، وإلا يمكنك ترميزها بشكل ثابت ولكنها تؤدي إلى إضافة أصفار إضافية إذا كان لديك عدد عشري أقل.مثال :

    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
    

    يؤدي استخدام اللغة الإنجليزية إلى التأكد من حصولك على نقطة للفاصل العشري، أينما سيتم تشغيل البرنامج

لماذا استخدام 340 ثم ل setMaximumFractionDigits ?

سببان :

  • setMaximumFractionDigits يقبل عددًا صحيحًا ولكن تنفيذه يحتوي على الحد الأقصى المسموح به من الأرقام DecimalFormat.DOUBLE_FRACTION_DIGITS والذي يساوي 340
  • Double.MIN_VALUE = 4.9E-324 لذلك، مع وجود 340 رقمًا، تأكد من عدم تقريب دقتك المزدوجة والفضفاضة

ولم لا:

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

يجب أن يعمل هذا مع القيم القصوى التي يدعمها Double.عائدات:

0.12
12
12.144252
0

سنتي 2:

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

على جهازي، تكون الوظيفة التالية أسرع بحوالي 7 مرات من الوظيفة التي يوفرها إجابة جيسون د, ، لأنه يتجنب String.format:

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

لا يهم.

فقدان الأداء بسبب معالجة السلسلة هو صفر.

وإليك الكود الذي سيقطع النهاية بعد ذلك %f

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

    return number.replaceAll("\\.?0*$", "");
}
if (d == Math.floor(d)) {
    return String.format("%.0f", d);
} else {
    return Double.toString(d);
}

يرجى ملاحظة ذلك String.format(format, args...) يكون تعتمد على اللغة لأنه تنسيقات باستخدام اللغة الافتراضية للمستخدم، وهذا هو، ربما مع الفواصل وحتى مسافات داخل مثل 123 456,789 أو 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;
}

انا صنعت DoubleFormatter لتحويل عدد كبير من القيم المزدوجة بكفاءة إلى سلسلة لطيفة/قابلة للعرض:

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.إذا كنت لا تستخدم 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();
}

إستخدم DecimalFormat و setMinimumFractionDigits(0)

هذا سوف ينجز المهمة بشكل جيد، أعلم أن الموضوع قديم، لكنني كنت أعاني من نفس المشكلة حتى وصلت إلى هذا.آمل أن يجدها شخص ما مفيدة.

    public static String removeZero(double number) {
        DecimalFormat format = new DecimalFormat("#.###########");
        return format.format(number);
    }
new DecimalFormat("00.#").format(20.236)
//out =20.2

new DecimalFormat("00.#").format(2.236)
//out =02.2
  1. 0 للحد الأدنى لعدد الأرقام
  2. يعرض # أرقامًا
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);
    }
}

إجابة متأخرة ولكن...

انت قلت انت يختار لتخزين أرقامك مع نوع مزدوج.أعتقد أن هذا قد يكون أصل المشكلة لأنه يجبرك على التخزين الأعداد الصحيحة إلى مضاعفات (وبالتالي فقدان المعلومات الأولية عن طبيعة القيمة).ماذا عن تخزين أرقامك في حالات رقم فئة (فئة فائقة لكل من 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
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 كان يعطيني مخالفة في تقرير السونار

هذا هو ما خطرت لي:

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

بطانة واحدة بسيطة، لا تُلقي إلا إذا لزم الأمر حقًا

فيما يلي طريقتان لتحقيق ذلك.أولاً، الطريقة الأقصر (وربما الأفضل):

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

إليك إجابة تعمل بالفعل (مجموعة من الإجابات المختلفة هنا)

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

أعرف أن هذا الموضوع قديم جدًا..لكن أعتقد أن أفضل طريقة للقيام بذلك هي كما يلي:

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 بتقريبه إلى آخر رقمين عشريين.لذلك سوف DecimalFormat.إذا كنت بحاجة إلى جميع المنازل العشرية ولكن ليس الأصفار الزائدة، فهذا يعمل بشكل أفضل.

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

وهذا سيجعل السلسلة لإسقاط المخلفات 0-s.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top