標籤:

【MaxCompute學習】隱式轉化的問題

有一次計算一個數據的百分比,想把小數結果取2位,並拼接一個百分號展示在結果報表中。用到的sql如下

select concat(round(10230/1497409,4)*100,%) from dual;

很奇怪局部數據並沒有保留2位小數,比如上面的數據返回的是67.99999999999999

我計算了下上面的結果大概得到的數據為0.0068

select concat(round(0.0066 ,4)*100,%) from dual;--0.66%select concat(round(0.0067 ,4)*100,%) from dual;--0.67%select concat(round(0.0068 ,4)*100,%) from dual;--0.6799999999999999%select concat(round(0.0069 ,4)*100,%) from dual;--0.69%

由於計算值採用了concat函數,concat的多個參數為string類型,如果輸入為bigint,decimal,double,datetime類型會隱式轉化為string類型,並且返回的為string類型

也就是說有兩種情況

1.round函數返回的數字cast為string後就丟失了精度。 2.round函數返回的數字就丟失了精度。

select round(0.0066 ,4)*100 from dual; --0.66select round(0.0067 ,4)*100 from dual; --0.67select round(0.0068 ,4)*100 from dual; --0.6799999999999999select round(0.0069 ,4)*100 from dual; --0.69

上面的結果說明是round函數返回的數字就丟失了精度。

round函數是用來計算指定到小數點位數的四捨五入的值的,如果其第一個參數為double類型,那麼函數計算的結果就是double類型,如果其第一個參數為Decimal類型,那麼函數計算的結果就是decimal類型的,如果其第一個參數為string類型或者bigint類型,那麼就會隱式轉化為double類型。

單獨計算round的返回值如下,說明double類型的round返回值為double

select round(0.0068 ,4) from dual; --0.0068

再計算乘法的結果,double和bigint相計算的時候也是會發生隱式轉化的

select 0.0066*100 from dual;--0.66select 0.0067*100 from dual;--0.67select 0.0068*100 from dual;--0.6799999999999999select 0.0069*100 from dual;--0.69

對於操作符號的運算,string,bigint和double都可以參與算術運算,string類型會轉成double類型計算 當bigint和bigint進行除法運算的時候結果會返回double類型,當bigint和double共同計算的時候,big今天會轉成doule類型,並且返回結果為double類型,

那麼100變成了double類型,這時候問題就有點眉目了

select cast(0.0068 as double) from dual;--0.0068select cast(100 as double) from dual;--100.0select 0.0068*100.0 from dual;--0.6799999999999999

double浮點數運算的時候會有丟失精度的問題,這個是所有的浮點數運算的通病,我們可以再java下驗證一下

public class TestDouble { public static void main(String args[]){ Double a=0.0068; Double b=100.0; System.out.println(a*b);//0.6799999999999999 Double c=0.0067; Double d=100.0; System.out.println(c*d);//0.67 BigDecimal e= new BigDecimal(0.0068); BigDecimal f=new BigDecimal(100.0); System.out.println(e.multiply(f));//0.679999999999999962113639284666533058043569326400756835937500 BigDecimal g= new BigDecimal(Double.toString(0.0068)); BigDecimal h=new BigDecimal(Double.toString(100.0)); System.out.println(g.multiply(h));//0.68000 }}

當然這個問題的最終解決方案很簡單,不過以後再計算對精度要求比較高的數據的時候建議還是設計成decimal類型

select round(10230*100/1497409,4) from dual;

本來選自阿里雲大數據產品專家「隱林」,擅長MaxCompute、機器學習、分散式、可視化、人工智慧等大數據領域。

推薦閱讀:

城市大數據下 智能交通的下一個風口
雨沐田:說說政府開放大數據
「電子足跡」如何預測城市發展?| 數據科學應用案例
iCloud雲上貴州,2018數博會連接2億多用戶
如何查詢PC28開獎網站歷史開獎數據呢?

TAG:大數據 |