Java中浮点数的基础知识
偶然查看Math.round的JDK
1 public static int round(float a) { 2 if (a != 0x1.fffffep-2f) // greatest float value less than 0.5 3 return (int)floor(a + 0.5f); 4 else 5 return 0; 6 }?
注釋說0x1.fffffep-2f是最接近0.5的float類型的小數,咦,科學計數法用e表示指數我是知道的,但是這個p是什么鬼。可能有的讀者還會問,為什么這個數時最接近0.5的數,這個數到底是多少呢?所以現在的問題有兩個:
1、p代表什么。
2、0x1.fffffep-2f 用十進制表示到底是多少。
先公布答案,P在16進制表示的浮點數中代替e作為科學計數法指數部分的標志,1.fffffep-2f中的e是十六進制中的14;第二個問題很簡單,也很復雜。說它簡單是因為只需要幾行代碼就可以知道該值是多少。
System.out.println( 0x1.fffffep-2f); BigDecimal bigDecimal=new BigDecimal(0x1.fffffep-2f); System.out.println(bigDecimal.toPlainString());/*輸出 0.49999997 0.4999999701976776123046875說它復雜是理解它為什么是最近0.5的float數。
?
1、P究竟是什么
原來為了和十六進制中的e進行區分,在java中用16進制表示的浮點數,我們用P代替e作為指數的標志。所以該常數代表0x1.fffffe * 2^(-2),f(F)是float后綴,不寫代表是double類型。
?A floating-point literal has the following parts: a whole-number part, a decimal or hexadecimal point (represented by an ASCII period character), a fractional part, an exponent, and a type suffix. A floating point number may be written either as a decimal value or as a hexadecimal value. For decimal literals, the exponent, if present, is indicated by the ASCII letter e or E followed by an optionally signed integer.?For hexadecimal literals, the exponent is always required and is indicated by the ASCII letter p or P followed by an optionally signed integer.
參考鏈接:https://stackoverflow.com/questions/8603232/p-in-constant-declaration/8603263#8603263(中文版Java語言規范3.10.2)
2、為什么是最接近0.5的數
任意一個二進制浮點數V可以表示成下面的形式:
(1)(-1)^s表示符號位,當s=0,V為正數;當s=1,V為負數。
(2)M表示尾數,范圍是[1,2)(規格化)或者是[0,1)(非規范化)。
(3)2^E表示階碼。
IEEE 754規定,對于32位的浮點數,最高的1位是符號位s,接著的8位是指數E,剩下的23位為有效數字M。e代表指數部分的無符號數ek-1ek-2...e0,f代表尾數部分的無符號數表示,0.fn-1...f1f0。
對于exp非零的數即規格化數而言,指數部分的取值范圍E=e-127,是[-126,127],尾數部分是[1,2-2^(-23)](M=1+f)。
要得到最接近0.5的數,我們不能用1*2(-1),因為是完全相等。退而求其次,我們用2-2^(-23)*2(-2),一個非常接近2的數(3.9999998)除以4來表示0.5無疑是最正確的選擇。
看到這里細心的讀者可能會問,為什么不用一個非常接近4的數去除以8來到答案呢?無論十六進制浮點數寫成0x1.fffffep2這種形式,還是寫成0x3.3fffff這種尾數大于2的,Java內部都會通過調整階碼自動將尾數部分控制在[1,2)之間。所以無論使用3.99999(近似表示,下同)除以8還是7.9999999除以16,最后都要表示為一個尾數在[1,2)范圍內,乘以一個階碼。換句話說,無論是用3.99999除以8還是7.9999999除以16最終的結果都是一樣的。為了看起來比較直觀,避免尾數轉換之后精度丟失的麻煩,我們直接將尾數固定在[1,2)去確定階碼無疑是最正確的選擇。
0x1.fffffep-2f二進制表示是 0 11111100 1111 1111 1111 1111 1111 111。
我們可以猜想對于double類型最接近0.5的數而言,應該同樣是尾數全是1,階碼為-2。即為0x1.fffffffffffffp-2。
public static long round(double a) {if (a != 0x1.fffffffffffffp-2) // greatest double value less than 0.5return (long)floor(a + 0.5d);elsereturn 0;}?
查看JDK,果不其然,說明我的分析是沒有問題的。
(以上基礎知識來自《深入理解計算機系統》2.4.2)
?
轉載于:https://www.cnblogs.com/BJUT-2010/p/5551008.html
總結
以上是生活随笔為你收集整理的Java中浮点数的基础知识的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java基础第十四天_IO
- 下一篇: 阶段会议2