日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

老大说:谁要再用double定义商品金额,就自己收拾东西走

發(fā)布時間:2024/3/26 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 老大说:谁要再用double定义商品金额,就自己收拾东西走 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.


先看現(xiàn)象

涉及諸如float或者double這兩種浮點型數(shù)據(jù)的處理時,偶爾總會有一些怪怪的現(xiàn)象,不知道大家注意過沒,舉幾個常見的栗子:

典型現(xiàn)象(一):條件判斷超預(yù)期

System.out.println( 1f == 0.9999999f ); // 打印:false System.out.println( 1f == 0.99999999f ); // 打印:true 納尼?

典型現(xiàn)象(二):數(shù)據(jù)轉(zhuǎn)換超預(yù)期

float f = 1.1f; double d = (double) f; System.out.println(f); // 打印:1.1 System.out.println(d); // 打印:1.100000023841858 納尼?

典型現(xiàn)象(三):基本運算超預(yù)期

System.out.println( 0.2 + 0.7 ); // 打印:0.8999999999999999 納尼?

典型現(xiàn)象(四):數(shù)據(jù)自增超預(yù)期

float f1 = 8455263f; for (int i = 0; i < 10; i++) {System.out.println(f1);f1++; } // 打印:8455263.0 // 打印:8455264.0 // 打印:8455265.0 // 打印:8455266.0 // 打印:8455267.0 // 打印:8455268.0 // 打印:8455269.0 // 打印:8455270.0 // 打印:8455271.0 // 打印:8455272.0float f2 = 84552631f; for (int i = 0; i < 10; i++) {System.out.println(f2);f2++; } // 打印:8.4552632E7 納尼?不是 +1了嗎? // 打印:8.4552632E7 納尼?不是 +1了嗎? // 打印:8.4552632E7 納尼?不是 +1了嗎? // 打印:8.4552632E7 納尼?不是 +1了嗎? // 打印:8.4552632E7 納尼?不是 +1了嗎? // 打印:8.4552632E7 納尼?不是 +1了嗎? // 打印:8.4552632E7 納尼?不是 +1了嗎? // 打印:8.4552632E7 納尼?不是 +1了嗎? // 打印:8.4552632E7 納尼?不是 +1了嗎? // 打印:8.4552632E7 納尼?不是 +1了嗎?

看到?jīng)],這些簡單場景下的使用情況都很難滿足我們的需求,所以說用浮點數(shù)(包括double和float)處理問題有非常多隱晦的坑在等著咱們!

怪不得技術(shù)總監(jiān)發(fā)狠話:誰要是敢在處理諸如 商品金額訂單交易、以及貨幣計算時用浮點型數(shù)據(jù)(double/float),直接讓我們走人!


原因出在哪里?

我們就以第一個典型現(xiàn)象為例來分析一下:

System.out.println( 1f == 0.99999999f );

直接用代碼去比較1和0.99999999,居然打印出true!

這說明了什么?這說明了計算機壓根區(qū)分不出來這兩個數(shù)。這是為什么呢?

我們不妨來簡單思考一下:

我們知道輸入的這兩個浮點數(shù)只是我們?nèi)祟惾庋鬯吹降木唧w數(shù)值,是我們通常所理解的十進(jìn)制數(shù),但是計算機底層在計算時可不是按照十進(jìn)制來計算的,學(xué)過基本計組原理的都知道,計算機底層最終都是基于像010100100100110011011這種0、1二進(jìn)制來完成的。

所以為了搞懂實際情況,我們應(yīng)該將這兩個十進(jìn)制浮點數(shù)轉(zhuǎn)化到二進(jìn)制空間來看一看。

十進(jìn)制浮點數(shù)轉(zhuǎn)二進(jìn)制 怎么轉(zhuǎn)、怎么計算,我想這應(yīng)該屬于基礎(chǔ)計算機進(jìn)制轉(zhuǎn)換常識,在 《計算機組成原理》 類似的課上肯定學(xué)過了,咱就不在此贅述了,直接給出結(jié)果(把它轉(zhuǎn)換到IEEE 754 Single precision 32-bit,也就float類型對應(yīng)的精度)

1.0(十進(jìn)制) ↓ 00111111 10000000 00000000 00000000(二進(jìn)制) ↓ 0x3F800000(十六進(jìn)制) 0.99999999(十進(jìn)制) ↓ 00111111 10000000 00000000 00000000(二進(jìn)制) ↓ 0x3F800000(十六進(jìn)制)

果不其然,這兩個十進(jìn)制浮點數(shù)的底層二進(jìn)制表示是一毛一樣的,怪不得==的判斷結(jié)果返回true!

但是1f == 0.9999999f返回的結(jié)果是符合預(yù)期的,打印false,我們也把它們轉(zhuǎn)換到二進(jìn)制模式下看看情況:

1.0(十進(jìn)制) ↓ 00111111 10000000 00000000 00000000(二進(jìn)制) ↓ 0x3F800000(十六進(jìn)制) 0.9999999(十進(jìn)制) ↓ 00111111 01111111 11111111 11111110(二進(jìn)制) ↓ 0x3F7FFFFE(十六進(jìn)制)

哦,很明顯,它倆的二進(jìn)制數(shù)字表示確實不一樣,這是理所應(yīng)當(dāng)?shù)慕Y(jié)果。

那么為什么0.99999999的底層二進(jìn)制表示竟然是:00111111 10000000 00000000 00000000 呢?

這不明明是浮點數(shù)1.0的二進(jìn)制表示嗎?

這就要談一下浮點數(shù)的精度問題了。


浮點數(shù)的精度問題!

學(xué)過 《計算機組成原理》 這門課的小伙伴應(yīng)該都知道,浮點數(shù)在計算機中的存儲方式遵循IEEE 754 浮點數(shù)計數(shù)標(biāo)準(zhǔn),可以用科學(xué)計數(shù)法表示為:

只要給出:符號(S)階碼部分(E)尾數(shù)部分(M) 這三個維度的信息,一個浮點數(shù)的表示就完全確定下來了,所以float和double這兩種浮點數(shù)在內(nèi)存中的存儲結(jié)構(gòu)如下所示:

1、符號部分(S)

0-正 1-負(fù)

2、階碼部分(E)(指數(shù)部分)

  • 對于float型浮點數(shù),指數(shù)部分8位,考慮可正可負(fù),因此可以表示的指數(shù)范圍為-127 ~ 128
  • 對于double型浮點數(shù),指數(shù)部分11位,考慮可正可負(fù),因此可以表示的指數(shù)范圍為-1023 ~ 1024

3、尾數(shù)部分(M)

浮點數(shù)的精度是由尾數(shù)的位數(shù)來決定的:

  • 對于float型浮點數(shù),尾數(shù)部分23位,換算成十進(jìn)制就是 2^23=8388608,所以十進(jìn)制精度只有6 ~ 7位;
  • 對于double型浮點數(shù),尾數(shù)部分52位,換算成十進(jìn)制就是 2^52 = 4503599627370496,所以十進(jìn)制精度只有15 ~ 16位

所以對于上面的數(shù)值0.99999999f,很明顯已經(jīng)超過了float型浮點數(shù)據(jù)的精度范圍,出問題也是在所難免的。


精度問題如何解決

所以如果涉及商品金額交易值貨幣計算等這種對精度要求很高的場景該怎么辦呢?

方法一:用字符串或者數(shù)組解決多位數(shù)問題

校招刷過算法題的小伙伴們應(yīng)該都知道,用字符串或者數(shù)組表示大數(shù)是一個典型的解題思路。

比如經(jīng)典面試題:編寫兩個任意位數(shù)大數(shù)的加法、減法、乘法等運算

這時候我們我們可以用字符串或者數(shù)組來表示這種大數(shù),然后按照四則運算的規(guī)則來手動模擬出具體計算過程,中間還需要考慮各種諸如:進(jìn)位借位符號等等問題的處理,確實十分復(fù)雜,本文不做贅述。

方法二:Java的大數(shù)類是個好東西

JDK早已為我們考慮到了浮點數(shù)的計算精度問題,因此提供了專用于高精度數(shù)值計算的大數(shù)類來方便我們使用。

在前文《不瞞你說,我最近跟Java源碼杠上了》中說過,Java的大數(shù)類位于java.math包下:

可以看到,常用的BigInteger 和 BigDecimal就是處理高精度數(shù)值計算的利器。

BigDecimal num3 = new BigDecimal( Double.toString( 0.1f ) ); BigDecimal num4 = new BigDecimal( Double.toString( 0.99999999f ) ); System.out.println( num3 == num4 ); // 打印 falseBigDecimal num1 = new BigDecimal( Double.toString( 0.2 ) ); BigDecimal num2 = new BigDecimal( Double.toString( 0.7 ) );// 加 System.out.println( num1.add( num2 ) ); // 打印:0.9// 減 System.out.println( num2.subtract( num1 ) ); // 打印:0.5// 乘 System.out.println( num1.multiply( num2 ) ); // 打印:0.14// 除 System.out.println( num2.divide( num1 ) ); // 打印:3.5

當(dāng)然了,像BigInteger 和 BigDecimal這種大數(shù)類的運算效率肯定是不如原生類型效率高,代價還是比較昂貴的,是否選用需要根據(jù)實際場景來評估。


總結(jié)

以上是生活随笔為你收集整理的老大说:谁要再用double定义商品金额,就自己收拾东西走的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。