1.四捨五入實數

正如我們已經討論過的,當一個實數被分配給一個int變量時,它總是被四捨五入到最接近的更小的整數——小數部分被簡單地丟棄。

但是很容易想像這樣一種情況,當一個小數需要在任一方向上四捨五入到最接近的整數,甚至四捨五入。在這種情況下你會怎麼做?

對於這種情況和許多類似的情況,Java 有類Math,它有round()ceil()floor()方法。


Math.round()方法

Math.round()方法將數字四捨五入為最接近的整數:

long x = Math.round(real_number)

但這裡還有另一個細微差別:此方法返回一個long整數(不是int)。因為實數可能非常大,Java 的創建者決定使用 Java 最大的可用整數類型:long.

因此,如果程序員想要將結果分配給一個int變量,那麼她必須明確地向編譯器表明她接受可能的數據丟失(如果結果數字不適合某種類型int)。

int x = (int) Math.round(real_number)

例子:

陳述 結果
int x = (int) Math.round(4.1);
4
int x = (int) Math.round(4.5);
5
int x = (int) Math.round(4.9);
5

Math.ceil()方法

Math.ceil()方法將數字四捨五入整數。以下是示例:

陳述 結果
int x = (int) Math.ceil(4.1);
5
int x = (int) Math.ceil(4.5);
5
int x = (int) Math.ceil(4.9);
5

Math.floor()方法

Math.floor()方法將數字向下舍入為整數。以下是示例:

陳述 結果
int x = (int) Math.floor(4.1);
4
int x = (int) Math.floor(4.5);
4
int x = (int) Math.floor(4.9);
4

當然,當將數字向下舍入為整數時,簡單地使用類型轉換運算符會更容易:(int)

陳述 結果
int x = (int) 4.9
4

如果您覺得很難記住這些名字,簡短的英語課會有所幫助:

  • Math意味著數學
  • Round表示圓形
  • Ceiling意味著天花板
  • Floor意味著地板

2. 浮點數的結構

該類型可以存儲從到double範圍內的值。這種巨大的值範圍(與類型相比)是由於類型(以及)具有與整數類型完全不同的內部結構這一事實。在內部,該類型將其值編碼為兩個數字:第一個稱為尾數第二個稱為指數-1.7*10308+1.7*10308intdoublefloatdouble

假設我們有數字123456789並將其存儲在一個double變量中。當我們這樣做時,數字被轉換為,並且在內部類型存儲兩個數字 —和。尾數(“數字的重要部分”或尾數)以紅色突出顯示,而指數以藍色突出顯示。1.23456789*108double234567898

這種方法可以存儲非常大的數字和非常小的數字。但是由於數字的表示限制為 8 個字節(64 位),並且其中一些位用於存儲指數以及尾數的符號和指數的符號),因此可用於表示尾數的最大位數15 歲

這是對實數結構的非常簡化的描述。


3. 處理實數時精度損失

使用實數時,請始終牢記實數 並不精確。從十進制轉換為二進制時,可能總是會出現舍入錯誤轉換錯誤。此外,最常見的錯誤來源是在完全不同的尺度上添加/減去數字時的精度損失

最後一個事實對於新手程序員來說有點令人興奮。

如果我們從 中減去,我們得到。1/109109109

在完全不同的尺度上減去數字 解釋
 1000000000.000000000;
-         0.000000001;
 1000000000.000000000;
第二個數字非常小,這將導致其有效數字(以灰色突出顯示)被忽略。15位有效數字以橙色突出顯示。

我們能說什麼,編程與數學不同。


4.比較實數時的陷阱

另一個危險在於等待程序員比較實數。它在處理實數時出現,因為捨入誤差會累積。結果是,在某些情況下,預期實數相等,但事實並非如此。反之亦然:數字應該不同,但它們是相等的。

例子:

陳述 解釋
double a = 1000000000.0;
double b = 0.000000001;
double c = a - b;
變量的值a 將是1000000000.0
變量的值c 將是1000000000.0
(變量中的數字b 過小)

在上面的例子中,aandc不應該相等,但它們是。

或者我們再舉一個例子:

陳述 解釋
double a = 1.00000000000000001;
double b = 1.00000000000000002;
變量的值a 將是1.0
變量的值b 將是1.0

5. 一個有趣的事實strictfp

Java有一個特殊的關鍵字strictfpstrict floating point ),這是其他編程語言所沒有的。你知道你為什麼需要它嗎?它會降低浮點數運算的準確性。這是它是如何形成的故事:

Java的創造者:
我們真的希望 Java 超級流行,並在盡可能多的設備上運行 Java 程序。所以我們確保 Java 機器的規範規定所有程序必須在所有類型的設備上以相同的方式運行!
英特爾處理器製造商:
嘿大家!我們改進了處理器,現在所有實數在我們的處理器中都使用 10 字節而不是 8 字節表示。更多字節意味著更多有效數字。這意味著什麼?這是正確的!現在你的科學計算會更加準確!
科學家和所有參與超精密計算的人:
涼爽的!做得好。好消息!
Java的創造者:
不不不,你們!我們已經告訴過您,所有 Java 程序必須在所有設備上以相同的方式運行。我們將強制禁用在 Intel 處理器中使用 10 字節實數的功能。
現在一切都好了!不要感謝我們。
科學家和所有參與超精密計算的人:
你完全瘋了嗎?趕緊把一切恢復原樣!
Java的創造者:
伙計們,這是為你們好!試想一下:所有 Java 程序在所有設備上的運行方式都相同。這太酷了!
科學家和所有參與超精密計算的人:
不,一點也不酷。趕緊把一切恢復原樣!或者知道我們會將您的 Java 放在哪裡?
Java的創造者:
唔。你為什麼不馬上說呢?當然,我們會把它放回去。
我們恢復了您使用最新處理器的所有功能的能力。
對了……我們還特地strictfp給語言加了關鍵詞。如果你把它寫在函數名之前,那麼在所有設備上該函數內涉及實數的所有操作都同樣糟糕