bigdicmal除法精度设置_BigDecimal 高精度计算 熟悉扩展,java除法保留小数问题
java保留兩位小數(shù)問題:
方式一:
四舍五入
double?? f?? =?? 111231.5585;
BigDecimal?? b?? =?? new?? BigDecimal(f);
double?? f1?? =?? b.setScale(2,?? BigDecimal.ROUND_HALF_UP).doubleValue();
保留兩位小數(shù)
---------------------------------------------------------------
方式二:
java.text.DecimalFormat?? df?? =new?? java.text.DecimalFormat("#.00");
df.format(你要格式化的數(shù)字);
例:new java.text.DecimalFormat("#.00").format(3.1415926)
#.00 表示兩位小數(shù) #.0000四位小數(shù) 以此類推...
方式三:
double d = 3.1415926;
String result = String .format("%.2f");
%.2f %. 表示 小數(shù)點前任意位數(shù)?? 2 表示兩位小數(shù) 格式后的結(jié)果為f 表示浮點型
方式四:
NumberFormat ddf1=NumberFormat.getNumberInstance() ;
void setMaximumFractionDigits(int digits)
digits 顯示的數(shù)字位數(shù)
為格式化對象設(shè)定小數(shù)點后的顯示的最多位,顯示的最后位是舍入的
import java.text.* ;
import java.math.* ;
class TT
{
public static void main(String args[])
{ double x=23.5455;
NumberFormat ddf1=NumberFormat.getNumberInstance() ;
ddf1.setMaximumFractionDigits(2);
String s= ddf1.format(x) ;
System.out.print(s);
}
}
---------------------------------------------------------------------------------------------------------
有一篇:
(1)、浮點數(shù)精確計算
勝利油田三流合一項目中一直存在一個問題,就是每次報表統(tǒng)計的物資金額和實際的金額要差那么幾分錢,和實際金額不一致,讓客戶覺得總是不那么舒服,原因是因為我們使用java的浮點類型double來定義物資金額,并且在報表統(tǒng)計中我們經(jīng)常要進行一些運算,但Java中浮點數(shù)(double、float)的計算是非精確計算,請看下面一個例子:
System.out.println(0.05 + 0.01);
System.out.println(1.0 - 0.42);
System.out.println(4.015 * 100);
System.out.println(123.3 / 100);
你的期望輸出是什么?可實際的輸出確實這樣的:
0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999
這個問題就非常嚴(yán)重了,如果你有123.3元要購買商品,而計算機卻認為你只有123.29999999999999元,錢不夠,計算機拒絕交易。
(2)、四舍五入
是否可以四舍五入呢?當(dāng)然可以,習(xí)慣上我們本能就會這樣考慮,但四舍五入意味著誤差,商業(yè)運算中可能意味著錯誤,同時Java中也沒有提供保留指定位數(shù)的四舍五入方法,只提供了一個Math.round(double d)和Math.round(float f)的方法,分別返回長整型和整型值。round方法不能設(shè)置保留幾位小數(shù),我們只能象這樣(保留兩位):
public double round(double value){
return Math.round( value * 100 ) / 100.0;
}
但非常不幸的是,上面的代碼并不能正常工作,給這個方法傳入4.015它將返回4.01而不是4.02,如我們在上面看到的
4.015 * 100 = 401.49999999999994
因此如果我們要做到精確的四舍五入,這種方法不能滿足我們的要求。
還有一種方式是使用java.text.DecimalFormat,但也存在問題,format采用的舍入模式是ROUND_HALF_DOWN(舍入模式在下面有介紹),比如說4.025保留兩位小數(shù)會是4.02,因為.025距離” nearest neighbor”(.02和.03)長度是相等,向下舍入就是.02,如果是4.0251那么保留兩位小數(shù)就是4.03。
System.out.println(new java.text.DecimalFormat("0.00").format(4.025));
System.out.println(new java.text.DecimalFormat("0.00").format(4.0251));
輸出是
4.02
4.03
(3)、浮點數(shù)輸出(科學(xué)記數(shù)法)
Java浮點型數(shù)值在大于9999999.0就自動轉(zhuǎn)化為科學(xué)記數(shù)法來表示,我們看下面的例子:
System.out.println(999999999.04);
System.out.println(99999999.04);
System.out.println(10000000.01);
System.out.println(9999999.04);
輸出的結(jié)果如下:
9.9999999904E8
9.999999904E7
1.000000001E7
9999999.04
但有時我們可能不需要科學(xué)記數(shù)法的表示方法,需要轉(zhuǎn)換為字符串,還不能直接用toString()等方法轉(zhuǎn)換,很煩瑣。
BigDecimal介紹
BigDecimal是Java提供的一個不變的、任意精度的有符號十進制數(shù)對象。它提供了四個構(gòu)造器,有兩個是用BigInteger構(gòu)造,在這里我們不關(guān)心,我們重點看用double和String構(gòu)造的兩個構(gòu)造器(有關(guān)BigInteger詳細介紹請查閱j2se API文檔)。
BigDecimal(double val)
Translates a double into a BigDecimal.
BigDecimal(String val)
Translates the String representation of a BigDecimal into a BigDecimal.
BigDecimal(double)是把一個double類型十進制數(shù)構(gòu)造為一個BigDecimal對象實例。
BigDecimal(String)是把一個以String表示的BigDecimal對象構(gòu)造為BigDecimal對象實例。
習(xí)慣上,對于浮點數(shù)我們都會定義為double或float,但BigDecimal API文檔中對于BigDecimal(double)有這么一段話:
Note: the results of this constructor can be somewhat unpredictable. One might assume that new BigDecimal(.1) is exactly equal to .1, but it is actually equal to .10000000000000000555111512312578 27021181583404541015625. This is so because .1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the long value that is being passed in to the constructor is not exactly equal to .1, appearances notwithstanding.
The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal(".1") is exactly equal to .1, as one would expect. Therefore, it is generally recommended that the (String) constructor be used in preference to this one
下面對這段話做簡單解釋:
注意:這個構(gòu)造器的結(jié)果可能會有不可預(yù)知的結(jié)果。有人可能設(shè)想new BigDecimal(.1)等于.1是正確的,但它實際上是等于.1000000000000000055511151231257827021181583404541015625,這就是為什么.1不能用一個double精確表示的原因,因此,這個被放進構(gòu)造器中的長值并不精確的等于.1,盡管外觀看起來是相等的。
然而(String)構(gòu)造器,則完全可預(yù)知的,new BigDecimal(“.1”)如同期望的那樣精確的等于.1,因此,(String)構(gòu)造器是被優(yōu)先推薦使用的。
看下面的結(jié)果:
System.out.println(new BigDecimal(123456789.02).toString());
System.out.println(new BigDecimal("123456789.02").toString());
輸出為:
123456789.01999999582767486572265625
123456789.02
現(xiàn)在我們知道,如果需要精確計算,非要用String來夠造BigDecimal不可!
實現(xiàn)方案
現(xiàn)在我們已經(jīng)知道怎么解決這個問題了,原則上是使用BigDecimal(String)構(gòu)造器,我們建議,在商業(yè)應(yīng)用開發(fā)中,涉及金額等浮點數(shù)計算的數(shù)據(jù),全部定義為String,數(shù)據(jù)庫中可定義為字符型字段,在需要使用這些數(shù)據(jù)進行運算的時候,使用BigDecimal(String)構(gòu)造BigDecimal對象進行運算,保證數(shù)據(jù)的精確計算。同時避免了科學(xué)記數(shù)法的出現(xiàn)。如果科學(xué)記數(shù)表示法在應(yīng)用中不是一種負擔(dān)的話,可以考慮定義為浮點類型。
這里我們提供了一個工具類,定義浮點數(shù)的加、減、乘、除和四舍五入等運算方法。以供參考。
源文件MathExtend.java:
import java.math.BigDecimal;
public class MathExtend
{
//默認除法運算精度
private static final int DEFAULT_DIV_SCALE = 10;
/**
* 提供精確的加法運算。
* @param v1
* @param v2
* @return 兩個參數(shù)的和
*/
public static double add(double v1, double v2)
{
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
/**
* 提供精確的加法運算
* @param v1
* @param v2
* @return 兩個參數(shù)數(shù)學(xué)加和,以字符串格式返回
*/
public static String add(String v1, String v2)
{
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.add(b2).toString();
}
/**
* 提供精確的減法運算。
* @param v1
* @param v2
* @return 兩個參數(shù)的差
*/
public static double subtract(double v1, double v2)
{
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
/**
* 提供精確的減法運算
* @param v1
* @param v2
* @return 兩個參數(shù)數(shù)學(xué)差,以字符串格式返回
*/
public static String subtract(String v1, String v2)
{
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.subtract(b2).toString();
}
/**
* 提供精確的乘法運算。
* @param v1
* @param v2
* @return 兩個參數(shù)的積
*/
public static double multiply(double v1, double v2)
{
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}
/**
* 提供精確的乘法運算
* @param v1
* @param v2
* @return 兩個參數(shù)的數(shù)學(xué)積,以字符串格式返回
*/
public static String multiply(String v1, String v2)
{
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.multiply(b2).toString();
}
/**
* 提供(相對)精確的除法運算,當(dāng)發(fā)生除不盡的情況時,精確到
* 小數(shù)點以后10位,以后的數(shù)字四舍五入,舍入模式采用ROUND_HALF_EVEN
* @param v1
* @param v2
* @return 兩個參數(shù)的商
*/
public static double divide(double v1, double v2)
{
return divide(v1, v2, DEFAULT_DIV_SCALE);
}
/**
* 提供(相對)精確的除法運算。當(dāng)發(fā)生除不盡的情況時,由scale參數(shù)指
* 定精度,以后的數(shù)字四舍五入。舍入模式采用ROUND_HALF_EVEN
* @param v1
* @param v2
* @param scale 表示需要精確到小數(shù)點以后幾位。
* @return 兩個參數(shù)的商
*/
public static double divide(double v1,double v2, int scale)
{
return divide(v1, v2, scale, BigDecimal.ROUND_HALF_EVEN);
}
/**
* 提供(相對)精確的除法運算。當(dāng)發(fā)生除不盡的情況時,由scale參數(shù)指
* 定精度,以后的數(shù)字四舍五入。舍入模式采用用戶指定舍入模式
* @param v1
* @param v2
* @param scale 表示需要精確到小數(shù)點以后幾位
* @param round_mode 表示用戶指定的舍入模式
* @return 兩個參數(shù)的商
*/
public static double divide(double v1,double v2,int scale, int round_mode){
if(scale < 0)
{
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2, scale, round_mode).doubleValue();
}
/**
* 提供(相對)精確的除法運算,當(dāng)發(fā)生除不盡的情況時,精確到
* 小數(shù)點以后10位,以后的數(shù)字四舍五入,舍入模式采用ROUND_HALF_EVEN
* @param v1
* @param v2
* @return 兩個參數(shù)的商,以字符串格式返回
*/
public static String divide(String v1, String v2)
{
return divide(v1, v2, DEFAULT_DIV_SCALE);
}
/**
* 提供(相對)精確的除法運算。當(dāng)發(fā)生除不盡的情況時,由scale參數(shù)指
* 定精度,以后的數(shù)字四舍五入。舍入模式采用ROUND_HALF_EVEN
* @param v1
* @param v2
* @param scale 表示需要精確到小數(shù)點以后幾位
* @return 兩個參數(shù)的商,以字符串格式返回
*/
public static String divide(String v1, String v2, int scale)
{
return divide(v1, v2, DEFAULT_DIV_SCALE, BigDecimal.ROUND_HALF_EVEN);
}
/**
* 提供(相對)精確的除法運算。當(dāng)發(fā)生除不盡的情況時,由scale參數(shù)指
* 定精度,以后的數(shù)字四舍五入。舍入模式采用用戶指定舍入模式
* @param v1
* @param v2
* @param scale 表示需要精確到小數(shù)點以后幾位
* @param round_mode 表示用戶指定的舍入模式
* @return 兩個參數(shù)的商,以字符串格式返回
*/
public static String divide(String v1, String v2, int scale, int round_mode)
{
if(scale < 0)
{
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.divide(b2, scale, round_mode).toString();
}
/**
* 提供精確的小數(shù)位四舍五入處理,舍入模式采用ROUND_HALF_EVEN
* @param v 需要四舍五入的數(shù)字
* @param scale 小數(shù)點后保留幾位
* @return 四舍五入后的結(jié)果
*/
public static double round(double v,int scale)
{
return round(v, scale, BigDecimal.ROUND_HALF_EVEN);
}
/**
* 提供精確的小數(shù)位四舍五入處理
* @param v 需要四舍五入的數(shù)字
* @param scale 小數(shù)點后保留幾位
* @param round_mode 指定的舍入模式
* @return 四舍五入后的結(jié)果
*/
public static double round(double v, int scale, int round_mode)
{
if(scale<0)
{
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
return b.setScale(scale, round_mode).doubleValue();
}
/**
* 提供精確的小數(shù)位四舍五入處理,舍入模式采用ROUND_HALF_EVEN
* @param v 需要四舍五入的數(shù)字
* @param scale 小數(shù)點后保留幾位
* @return 四舍五入后的結(jié)果,以字符串格式返回
*/
public static String round(String v, int scale)
{
return round(v, scale, BigDecimal.ROUND_HALF_EVEN);
}
/**
* 提供精確的小數(shù)位四舍五入處理
* @param v 需要四舍五入的數(shù)字
* @param scale 小數(shù)點后保留幾位
* @param round_mode 指定的舍入模式
* @return 四舍五入后的結(jié)果,以字符串格式返回
*/
public static String round(String v, int scale, int round_mode)
{
if(scale<0)
{
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(v);
return b.setScale(scale, round_mode).toString();
}
}
BigDecimal 舍入模式(Rounding mode)介紹:
BigDecimal定義了一下舍入模式,只有在作除法運算或四舍五入時才用到舍入模式,下面簡單介紹,詳細請查閱J2se API文檔
static int
ROUND_CEILING
Rounding mode to round towards positive infinity.
向正無窮方向舍入
static int
ROUND_DOWN
Rounding mode to round towards zero.
向零方向舍入
static int
ROUND_FLOOR
Rounding mode to round towards negative infinity.
向負無窮方向舍入
static int
ROUND_HALF_DOWN
Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.
向(距離)最近的一邊舍入,除非兩邊(的距離)是相等,如果是這樣,向下舍入, 例如1.55 保留一位小數(shù)結(jié)果為1.5
static int
ROUND_HALF_EVEN
Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor.
向(距離)最近的一邊舍入,除非兩邊(的距離)是相等,如果是這樣,如果保留位數(shù)是奇數(shù),使用ROUND_HALF_UP ,如果是偶數(shù),使用ROUND_HALF_DOWN
static int
ROUND_HALF_UP
Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
向(距離)最近的一邊舍入,除非兩邊(的距離)是相等,如果是這樣,向上舍入, 1.55保留一位小數(shù)結(jié)果為1.6
static int
ROUND_UNNECESSARY
Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary.
計算結(jié)果是精確的,不需要舍入模式
static int
ROUND_UP
Rounding mode to round away from zero.
向遠離0的方向舍入
BigDecimal類型的可以轉(zhuǎn)換到double類型嗎?如何轉(zhuǎn)換
有方法 java.math.BigDecimal.doubleValue()
BigDecimal a = new BigDecimal(1000);
return a.doubleValue();
BigDecimal類
對于不需要任何準(zhǔn)確計算精度的數(shù)字可以直接使用float或double,但是如果需要精確計算的結(jié)果,則必須使用BigDecimal類,而且使用BigDecimal類也可以進行大數(shù)的操作。BigDecimal類的常用方法如表11-15所示。
表11-15 BigDecimal類的常用方法
序號
方????法
類型
描????述
1
public BigDecimal(double val)
構(gòu)造
將double表示形式轉(zhuǎn)換
為BigDecimal
2
public BigDecimal(int val)
構(gòu)造
將int表示形式轉(zhuǎn)換為
BigDecimal
3
public BigDecimal(String val)
構(gòu)造
將字符串表示
形式轉(zhuǎn)換為BigDecimal
4
public BigDecimal add(BigDecimal augend)
普通
加法
5
public BigDecimal subtract(BigDecimal
subtrahend)
普通
減法
6
public BigDecimal multiply(BigDecimal
multiplicand)
普通
乘法
7
public BigDecimal divide(BigDecimal
divisor)
普通
除法
范例:進行四舍五入的四則運算
package org.lxh.demo11.numberdemo;
import java.math.BigDecimal;
class MyMath {
public static double add(double d1, double d2)
{ // 進行加法運算
BigDecimal b1 = new BigDecimal(d1);
BigDecimal b2 = new BigDecimal(d2);
return b1.add(b2).doubleValue();
}
public static double sub(double d1, double d2)
{ // 進行減法運算
BigDecimal b1 = new BigDecimal(d1);
BigDecimal b2 = new BigDecimal(d2);
return b1.subtract(b2).doubleValue();
}
public static double mul(double d1, double d2)
{ // 進行乘法運算
BigDecimal b1 = new BigDecimal(d1);
BigDecimal b2 = new BigDecimal(d2);
return b1.multiply(b2).doubleValue();
}
public static double div(double d1,
double d2,int len) {// 進行除法運算
BigDecimal b1 = new BigDecimal(d1);
BigDecimal b2 = new BigDecimal(d2);
return b1.divide(b2,len,BigDecimal.
ROUND_HALF_UP).doubleValue();
}
public static double round(double d,
int len) { // 進行四舍五入
操作
BigDecimal b1 = new BigDecimal(d);
BigDecimal b2 = new BigDecimal(1);
// 任何一個數(shù)字除以1都是原數(shù)字
// ROUND_HALF_UP是BigDecimal的一個常量,
表示進行四舍五入的操作
return b1.divide(b2, len,BigDecimal.
ROUND_HALF_UP).doubleValue();
}
}
public class BigDecimalDemo01 {
public static void main(String[] args) {
System.out.println("加法運算:" +
MyMath.round(MyMath.add(10.345,
3.333), 1));
System.out.println("乘法運算:" +
MyMath.round(MyMath.mul(10.345,
3.333), 3));
System.out.println("除法運算:" +
MyMath.div(10.345, 3.333, 3));
System.out.println("減法運算:" +
MyMath.round(MyMath.sub(10.345,
3.333), 3));
}
}
BigDecimal是Java中用來表示任意精確浮點數(shù)運算的類,在BigDecimal中,使用unscaledValue × 10-scale來表示一個浮點數(shù)。其中,unscaledValue是一個BigInteger,scale是一個int。從這個表示方法來看,BigDecimal只能標(biāo)識有限小數(shù),不過可以表示的數(shù)據(jù)范圍遠遠大于double,在實際應(yīng)用中基本足夠了。
下面提一下兩個精度問題:
System.out.println(new BigDecimal(0.1).toString()); // 0.1000000000000000055511151231257827021181583404541015625
System.out.println(new BigDecimal("0.1").toString()); // 0.1
System.out.println(new BigDecimal(
Double.toString(0.1000000000000000055511151231257827021181583404541015625)).toString());// 0.1
System.out.println(new BigDecimal(Double.toString(0.1)).toString()); // 0.1
分析一下上面代碼的問題(注釋的內(nèi)容表示此語句的輸出)
第一行:事實上,由于二進制無法精確地表示十進制小數(shù)0.1,但是編譯器讀到字符串"0.1"之后,必須把它轉(zhuǎn)成8個字節(jié)的double值,因此,編譯器只能用一個最接近的值來代替0.1了,即0.1000000000000000055511151231257827021181583404541015625。因此,在運行時,傳給BigDecimal構(gòu)造函數(shù)的真正的數(shù)值是0.1000000000000000055511151231257827021181583404541015625。
第二行:BigDecimal能夠正確地把字符串轉(zhuǎn)化成真正精確的浮點數(shù)。
第三行:問題在于Double.toString會使用一定的精度來四舍五入double,然后再輸出。會。Double.toString(0.1000000000000000055511151231257827021181583404541015625)輸出的事實上是"0.1",因此生成的BigDecimal表示的數(shù)也是0.1。
第四行:基于前面的分析,事實上這一行代碼等價于第三行
結(jié)論:
1.如果你希望BigDecimal能夠精確地表示你希望的數(shù)值,那么一定要使用字符串來表示小數(shù),并傳遞給BigDecimal的構(gòu)造函數(shù)。
2.如果你使用Double.toString來把double轉(zhuǎn)化字符串,然后調(diào)用BigDecimal(String),這個也是不靠譜的,它不一定按你的想法工作。
3.如果你不是很在乎是否完全精確地表示,并且使用了BigDecimal(double),那么要注意double本身的特例,double的規(guī)范本身定義了幾個特殊的double值(Infinite,-Infinite,NaN),不要把這些值傳給BigDecimal,否則會拋出異常。
問題二:把double強制轉(zhuǎn)化成int,難道不是扔掉小數(shù)部分嗎?
int x=(int)1023.99999999999999; // x=1024為什么?
原因還是在于二進制無法精確地表示某些十進制小數(shù),因此1023.99999999999999在編譯之后的double值變成了1024。
所以,把double強制轉(zhuǎn)化成int確實是扔掉小數(shù)部分,但是你寫在代碼中的值,并不一定是編譯器生成的真正的double值。
驗證代碼:
double d = 1023.99999999999999;
int x = (int) d;
System.out.println(new BigDecimal(d).toString()); // 1024
System.out.println(Long.toHexString(Double.doubleToRawLongBits(d))); // 4090000000000000
System.out.println(x); // 1024
前面提過BigDecimal可以精確地把double表示出來還記得吧。
我們也可以直接打印出d的二進制形式,根據(jù)IEEE 754的規(guī)定,我們可以算出0x4090000000000000=(1024)。
總結(jié)
以上是生活随笔為你收集整理的bigdicmal除法精度设置_BigDecimal 高精度计算 熟悉扩展,java除法保留小数问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机和单片机概述,微机与单片机概述课件
- 下一篇: 加密 lua_三、Lua相关知识