Apa yang aku suka adalah metode untuk mengkonversi ganda untuk string yang putaran menggunakan setengah hingga metode - yaitu jika desimal yang akan dibulatkan adalah 5, itu selalu putaran ke nomor berikutnya. Ini adalah standar metode pembulatan sebagian besar orang harapkan dalam kebanyakan situasi.
Saya juga ingin hanya signifikan digit yang akan ditampilkan, yaitu tidak boleh ada trailing nol.
Saya tahu salah satu cara untuk melakukan ini adalah dengan menggunakan String.format
metode:
String.format("%.5g%n", 0.912385);
kembali:
0.91239
yang besar, namun itu selalu menampilkan angka dengan 5 desimal bahkan jika mereka tidak signifikan:
String.format("%.5g%n", 0.912300);
kembali:
0.91230
Metode lain adalah dengan menggunakan DecimalFormatter
:
DecimalFormat df = new DecimalFormat("#.#####");
df.format(0.912385);
kembali:
0.91238
Namun seperti yang anda lihat ini menggunakan setengah bahkan pembulatan. Yang akan putaran bawah jika sebelumnya digit genap. Apa yang saya'a seperti ini:
0.912385 -> 0.91239
0.912300 -> 0.9123
Apa cara terbaik untuk mencapai hal ini di pulau Jawa?
Gunakan setRoundingMode
, mengatur RoundingMode
secara eksplisit untuk menangani masalah anda dengan setengah-setengah bahkan bulat, kemudian menggunakan format pola untuk output yang diperlukan.
Contoh:
DecimalFormat df = new DecimalFormat("#.####");
df.setRoundingMode(RoundingMode.CEILING);
for (Number n : Arrays.asList(12, 123.12345, 0.23, 0.1, 2341234.212431324)) {
Double d = n.doubleValue();
System.out.println(df.format(d));
}
memberikan output:
12
123.1235
0.23
0.1
2341234.2125
EDIT: asli jawabannya tidak alamat akurasi dari nilai-nilai ganda. Yang baik-baik saja jika anda don't peduli apakah itu putaran atas atau bawah. Tetapi jika anda ingin akurat pembulatan, maka anda perlu untuk mengambil diharapkan akurasi dari nilai-nilai ke rekening. Nilai-nilai Floating point memiliki representasi biner internal. Yang berarti bahwa nilai seperti 2.77395 tidak benar-benar memiliki nilai yang tepat secara internal. Hal ini dapat menjadi sedikit lebih besar atau sedikit lebih kecil. Jika nilai internal adalah sedikit lebih kecil, maka akan tidak bulat hingga 2.7740. Untuk memperbaiki situasi itu, anda perlu mengetahui keakuratan dari nilai-nilai yang anda bekerja dengan, dan menambah atau mengurangi nilai yang sebelum pembulatan. Misalnya, ketika anda tahu bahwa anda nilai-nilai yang akurat sampai dengan 6 digit, maka untuk putaran setengah-setengah nilai-nilai up, menambahkan bahwa akurasi dengan nilai:
Double d = n.doubleValue() + 1e-6;
Untuk putaran bawah, mengurangi akurasi.
new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);
anda akan mendapatkan BigDecimal
. Untuk mendapatkan string dari itu, hanya menyebut bahwa BigDecimal
's toString
metode, atau toPlainString
metode untuk Jawa 5+ untuk polos format string.
Contoh program:
package trials;
import java.math.BigDecimal;
public class Trials {
public static void main(String[] args) {
int yourScale = 10;
System.out.println(BigDecimal.valueOf(0.42344534534553453453-0.42324534524553453453).setScale(yourScale, BigDecimal.ROUND_HALF_UP));
}
Seperti beberapa orang lain telah mencatat, jawaban yang benar adalah untuk menggunakan DecimalFormat
atau BigDecimal
. Floating-point doesn't memiliki tempat desimal sehingga anda tidak mungkin bulat/truncate untuk sejumlah tertentu dari mereka di tempat pertama. Anda harus bekerja dalam desimal radix, dan itu adalah apa yang orang-orang dua kelas yang dilakukan.
Saya posting kode berikut sebagai counter-contoh untuk semua jawaban di thread ini dan memang seluruh StackOverflow (dan di tempat lain) yang merekomendasikan perkalian diikuti dengan pemotongan diikuti oleh divisi. Itu adalah kewajiban para pendukung teknik ini untuk menjelaskan mengapa kode berikut akan menghasilkan output yang salah dalam lebih dari 92% dari kasus.
public class RoundingCounterExample
{
static float roundOff(float x, int position)
{
float a = x;
double temp = Math.pow(10.0, position);
a *= temp;
a = Math.round(a);
return (a / (float)temp);
}
public static void main(String[] args)
{
float a = roundOff(0.0009434f,3);
System.out.println("a="+a+" (a % .001)="+(a % 0.001));
int count = 0, errors = 0;
for (double x = 0.0; x < 1; x += 0.0001)
{
count++;
double d = x;
int scale = 2;
double factor = Math.pow(10, scale);
d = Math.round(d * factor) / factor;
if ((d % 0.01) != 0.0)
{
System.out.println(d + " " + (d % 0.01));
errors++;
}
}
System.out.println(count + " trials " + errors + " errors");
}
}
Output dari program ini:
10001 trials 9251 errors
EDIT: Untuk mengatasi beberapa komentar di bawah ini saya redid modulus bagian dari tes loop menggunakan BigDecimal
dan baru MathContext(16)
untuk modulus operasi sebagai berikut:
public static void main(String[] args)
{
int count = 0, errors = 0;
int scale = 2;
double factor = Math.pow(10, scale);
MathContext mc = new MathContext(16, RoundingMode.DOWN);
for (double x = 0.0; x < 1; x += 0.0001)
{
count++;
double d = x;
d = Math.round(d * factor) / factor;
BigDecimal bd = new BigDecimal(d, mc);
bd = bd.remainder(new BigDecimal("0.01"), mc);
if (bd.multiply(BigDecimal.valueOf(100)).remainder(BigDecimal.ONE, mc).compareTo(BigDecimal.ZERO) != 0)
{
System.out.println(d + " " + bd);
errors++;
}
}
System.out.println(count + " trials " + errors + " errors");
}
Hasilnya:
10001 trials 4401 errors
Misalkan anda telah
double d = 9232.129394d;
anda dapat menggunakan BigDecimal
BigDecimal bd = new BigDecimal(d).setScale(2, RoundingMode.HALF_EVEN);
d = bd.doubleValue();
atau tanpa BigDecimal
d = Math.round(d*100)/100.0d;
dengan kedua solusi d == 9232.13
@Milhous: format desimal untuk pembulatan adalah:
Anda juga dapat menggunakan
DecimalFormat df = new DecimalFormat("#.00000"); df.format(0.912385);
untuk memastikan anda memiliki trailing 0's.
Saya akan menambahkan bahwa metode ini sangat baik untuk menyediakan aktual numerik, pembulatan mekanisme - tidak hanya secara visual, tetapi juga saat pengolahan.
Hipotesis: anda harus menerapkan mekanisme pembulatan ke GUI program. Untuk mengubah akurasi / presisi dari hasil output hanya perubahan caret format (yaitu dalam tanda kurung). Sehingga:
DecimalFormat df = new DecimalFormat("#0.######");
df.format(0.912385);
akan kembali sebagai output: 0.912385
DecimalFormat df = new DecimalFormat("#0.#####");
df.format(0.912385);
akan kembali sebagai output: 0.91239
DecimalFormat df = new DecimalFormat("#0.####");
df.format(0.912385);
akan kembali sebagai output: 0.9124
[EDIT: juga jika caret format seperti begitu ("#0.############") dan anda
masukkan desimal, misalnya 3.1415926, untuk argumen's sake, DecimalFormat
tidak menghasilkan sampah (misalnya akhiran angka nol) dan akan kembali:
3.1415926
.. jika anda're itu cenderung. Memang, itu's sedikit verbose
untuk keinginan dari beberapa dev's - tapi hei, itu's punya jejak memori rendah
selama pengolahan dan sangat mudah untuk menerapkan.]
Jadi pada dasarnya, keindahan DecimalFormat adalah bahwa hal itu secara bersamaan menangani string penampilan - serta tingkat pembulatan presisi yang ditetapkan. Ergo: anda mendapatkan dua manfaat untuk harga satu implementasi kode. ;)
Anda bisa menggunakan berikut utilitas metode-
public static double round(double valueToRound, int numberOfDecimalPlaces)
{
double multipicationFactor = Math.pow(10, numberOfDecimalPlaces);
double interestedInZeroDPs = valueToRound * multipicationFactor;
return Math.round(interestedInZeroDPs) / multipicationFactor;
}
Berikut adalah ringkasan dari apa yang dapat anda gunakan jika anda ingin hasil sebagai String:
DecimalFormat df = new DecimalFormat("#.#####"); df.setRoundingMode(RoundingMode.HALF_UP); String str1 = df.format(0.912385)); // 0.91239
String str2 = new BigDecimal(0.912385) .setScale(5, BigDecimal.ROUND_HALF_UP) .toString();
Berikut ini adalah saran dari apa yang perpustakaan dapat anda gunakan jika anda ingin double
sebagai hasilnya. Saya tidak't merekomendasikan hal ini untuk konversi string, meskipun, sebagai ganda mungkin tidak dapat mewakili apa yang tepat yang anda inginkan (lihat misalnya di sini):
ganda bulat = Presisi.bulat(0.912385, 5, BigDecimal.ROUND_HALF_UP);
ganda bulat = Fungsi.bulat(0.00001).menerapkan(0.912385)
ganda bulat = Utils.roundDouble(0.912385, 5)
Ringkas solusi:
public static double round(double value, int precision) {
int scale = (int) Math.pow(10, precision);
return (double) Math.round(value * scale) / scale;
}
Lihat juga, https://stackoverflow.com/a/22186845/212950 Terima kasih untuk jpdymond untuk menawarkan ini.
Anda dapat menggunakan BigDecimal
BigDecimal value = new BigDecimal("2.3");
value = value.setScale(0, RoundingMode.UP);
BigDecimal value1 = new BigDecimal("-2.3");
value1 = value1.setScale(0, RoundingMode.UP);
System.out.println(value + "n" + value1);
Lihat: http://www.javabeat.net/precise-rounding-of-decimals-using-rounding-mode-enumeration/
Jika anda benar-benar ingin angka desimal untuk perhitungan (dan tidak hanya untuk output), tidak menggunakan binary berbasis format floating point seperti double.
Use BigDecimal or any other decimal-based format.
Saya menggunakan BigDecimal untuk perhitungan, tapi ingatlah hal ini tergantung pada ukuran angka yang anda're berurusan dengan. Di sebagian besar implementasi, aku menemukan parsing dari dua atau integer ke Long yang cukup memadai untuk jumlah yang sangat besar perhitungan.
Pada kenyataannya, saya've baru-baru ini digunakan diurai-untuk-Lama untuk mendapatkan representasi akurat (sebagai lawan untuk hex hasil) dalam sebuah GUI untuk angka yang lebih besar seperti ################################# karakter (sebagai contoh).
Karena saya tidak menemukan jawaban yang lengkap pada tema ini saya've menempatkan bersama-sama kelas yang harus menangani hal ini dengan baik, dengan dukungan untuk:
Penggunaan cukup sederhana:
(Untuk kepentingan contoh ini saya menggunakan custom lokal)
public static final int DECIMAL_PLACES = 2;
NumberFormatter formatter = new NumberFormatter(DECIMAL_PLACES);
String value = formatter.format(9.319); // "9,32"
String value2 = formatter.format(0.0000005); // "5,00E-7"
String value3 = formatter.format(1324134123); // "1,32E9"
double parsedValue1 = formatter.parse("0,4E-2", 0); // 0.004
double parsedValue2 = formatter.parse("0,002", 0); // 0.002
double parsedValue3 = formatter.parse("3423,12345", 0); // 3423.12345
Berikut adalah kelas:
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.Locale;
public class NumberFormatter {
private static final String SYMBOL_INFINITE = "\u221e";
private static final char SYMBOL_MINUS = '-';
private static final char SYMBOL_ZERO = '0';
private static final int DECIMAL_LEADING_GROUPS = 10;
private static final int EXPONENTIAL_INT_THRESHOLD = 1000000000; // After this value switch to exponential notation
private static final double EXPONENTIAL_DEC_THRESHOLD = 0.0001; // Below this value switch to exponential notation
private DecimalFormat decimalFormat;
private DecimalFormat decimalFormatLong;
private DecimalFormat exponentialFormat;
private char groupSeparator;
public NumberFormatter(int decimalPlaces) {
configureDecimalPlaces(decimalPlaces);
}
public void configureDecimalPlaces(int decimalPlaces) {
if (decimalPlaces <= 0) {
throw new IllegalArgumentException("Invalid decimal places");
}
DecimalFormatSymbols separators = new DecimalFormatSymbols(Locale.getDefault());
separators.setMinusSign(SYMBOL_MINUS);
separators.setZeroDigit(SYMBOL_ZERO);
groupSeparator = separators.getGroupingSeparator();
StringBuilder decimal = new StringBuilder();
StringBuilder exponential = new StringBuilder("0.");
for (int i = 0; i < DECIMAL_LEADING_GROUPS; i++) {
decimal.append("###").append(i == DECIMAL_LEADING_GROUPS - 1 ? "." : ",");
}
for (int i = 0; i < decimalPlaces; i++) {
decimal.append("#");
exponential.append("0");
}
exponential.append("E0");
decimalFormat = new DecimalFormat(decimal.toString(), separators);
decimalFormatLong = new DecimalFormat(decimal.append("####").toString(), separators);
exponentialFormat = new DecimalFormat(exponential.toString(), separators);
decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
decimalFormatLong.setRoundingMode(RoundingMode.HALF_UP);
exponentialFormat.setRoundingMode(RoundingMode.HALF_UP);
}
public String format(double value) {
String result;
if (Double.isNaN(value)) {
result = "";
} else if (Double.isInfinite(value)) {
result = String.valueOf(SYMBOL_INFINITE);
} else {
double absValue = Math.abs(value);
if (absValue >= 1) {
if (absValue >= EXPONENTIAL_INT_THRESHOLD) {
value = Math.floor(value);
result = exponentialFormat.format(value);
} else {
result = decimalFormat.format(value);
}
} else if (absValue < 1 && absValue > 0) {
if (absValue >= EXPONENTIAL_DEC_THRESHOLD) {
result = decimalFormat.format(value);
if (result.equalsIgnoreCase("0")) {
result = decimalFormatLong.format(value);
}
} else {
result = exponentialFormat.format(value);
}
} else {
result = "0";
}
}
return result;
}
public String formatWithoutGroupSeparators(double value) {
return removeGroupSeparators(format(value));
}
public double parse(String value, double defValue) {
try {
return decimalFormat.parse(value).doubleValue();
} catch (ParseException e) {
e.printStackTrace();
}
return defValue;
}
private String removeGroupSeparators(String number) {
return number.replace(String.valueOf(groupSeparator), "");
}
}
Coba ini: org.apache.commons.math3.util.Presisi.round(double x, int skala)
Lihat: http://commons.apache.org/proper/commons-math/apidocs/org/apache/commons/math3/util/Precision.html
Apache Commons Matematika Perpustakaan homepage adalah: http://commons.apache.org/proper/commons-math/index.html
Internal implementasi dari metode ini adalah:
public static double round(double x, int scale) {
return round(x, scale, BigDecimal.ROUND_HALF_UP);
}
public static double round(double x, int scale, int roundingMethod) {
try {
return (new BigDecimal
(Double.toString(x))
.setScale(scale, roundingMethod))
.doubleValue();
} catch (NumberFormatException ex) {
if (Double.isInfinite(x)) {
return x;
} else {
return Double.NaN;
}
}
}
Hanya dalam kasus seseorang masih membutuhkan bantuan dengan ini. Solusi ini bekerja dengan sempurna untuk saya.
private String withNoTrailingZeros(final double value, final int nrOfDecimals) {
return new BigDecimal(String.valueOf(value)).setScale(nrOfDecimals, BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString();
}
mengembalikan String
dengan keluaran yang diinginkan.
Untuk mencapai hal ini kita dapat menggunakan formatter:
DecimalFormat df = new DecimalFormat("#.00");
String resultado = df.format(valor)
atau:
DecimalFormat df = new DecimalFormat("0.00"); :
Gunakan metode ini untuk selalu mendapatkan dua desimal:
private static String getTwoDecimals(double value){
DecimalFormat df = new DecimalFormat("0.00");
return df.format(value);
}
Mendefinisikan nilai-nilai ini:
91.32
5.22
11.5
1.2
2.6
Dengan menggunakan metode ini kita bisa mendapatkan hasil ini:
91.32
5.22
11.50
1.20
2.60
Saya setuju dengan pilihan jawaban menggunakan DecimalFormat
--- atau alternatif BigDecimal
.
Silakan baca Update di bawah ini terlebih dahulu!
Namun jika anda do ingin untuk putaran dua nilai dan mendapatkan double
nilai hasilnya, anda dapat menggunakan org.apache.commons.math3.util.Presisi.bulat(..)
seperti yang disebutkan di atas. Implementasi ini menggunakan BigDecimal
, lambat dan menciptakan sampah.
Serupa tapi cepat dan sampah-metode gratis yang disediakan oleh DoubleRounder
utilitas di decimal4j perpustakaan:
double a = DoubleRounder.round(2.0/3.0, 3);
double b = DoubleRounder.round(2.0/3.0, 3, RoundingMode.DOWN);
double c = DoubleRounder.round(1000.0d, 17);
double d = DoubleRounder.round(90080070060.1d, 9);
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
Akan output
0.667
0.666
1000.0
9.00800700601E10
Lihat https://github.com/tools4j/decimal4j/wiki/DoubleRounder-Utility
Disclaimer: saya terlibat dalam decimal4j proyek.
Update:
Seperti @iaforek menunjukkan DoubleRounder kadang-kadang kembali berlawanan dengan intuisi hasil. Alasannya adalah bahwa ia melakukan matematis benar pembulatan. Misalnya DoubleRounder.bulat(256.025 d, 2)
akan dibulatkan ke bawah untuk 256.02 karena nilai ganda direpresentasikan sebagai 256.025 d agak lebih kecil dari nilai rasional 256.025 dan karenanya akan dibulatkan ke bawah.
Catatan:
BigDecimal(double)
constructor (tapi tidak untuk valueOf(double)
yang menggunakan string konstruktor).Untuk alasan tersebut dan segala sesuatu yang disebutkan di atas di posting ini saya tidak merekomendasikan untuk menggunakan DoubleRounder.