簡體   English   中英

四舍五入的 function 如何在 Smalltalk 中工作?

[英]How does rounded function work in Smalltalk?

如何在 Smalltalk 中將小數點四舍五入(小數點后 3 位)。

代碼:

rounded
  "Answer an Integer nearest to receiver"
  ^(self + (self sign * 0.5)) truncated

呼叫者:

cbPrintPercentageString
   ^AbtpercentageConverter new
    primObjectToPrint: self

被叫方:

primObjectToPrint: anInput 
   **^(super primObjectToPrint: anInput*100), '%'** 

輸入1

anInput: 0.014545Datatype: Float

Output: 1.455% (被調用者返回的字符串)

解釋: 0.014545*100=1.4545 , 1.4545舍入到小數點后3位,即 output 1.455%

輸入2:

輸入: **0.018045** (數據DataType: float

Output: **1.804%**String

解釋: 0.018045*100=1.8045 ,當1.8045四舍五入到小數點后3位時,我期待1.805% ,但 UI 顯示1.804%

由於您正在處理浮點數,因此請注意,將為您提供正確舍入結果的算法並不像人們想象的那么簡單。

此外,根據 Smalltalk 方言及其版本,不保證:

  • 從 ASCII 十進制到二進制浮點數的轉換正確舍入
  • 從二進制浮點數到 ASCII 十進制數的轉換被正確舍入

至少,在最近的 Squeak 或 Pharo 圖像中,上述兩個操作將被正確舍入。 因此,下面的所有解釋都內置在 Squeak 5.x 或 6.x 中。

遇到的第一個問題是二進制浮點數不能准確表示兩個十進制數 0.014545 和 0.018045。
在 Squeak 中,這兩個表達式是錯誤的:

(Fraction readFrom: '0.014545' readStream) isAnExactFloat.
(Fraction readFrom: '0.018045' readStream) isAnExactFloat.

在 Squeak 中,這兩個不等式是正確的,這可能會提供有關將小數四舍五入到最接近的(可表示的)浮點數時使用的舍入方向的線索:

(Float readFrom: '0.014545' readStream) > (Fraction readFrom: '0.014545' readStream).
(Float readFrom: '0.018045' readStream) < (Fraction readFrom: '0.018045' readStream).

在 Squeak 中,浮點打印作為最短的十進制表示,可以轉換回相同的浮點(往返轉換),而不是浮點攜帶的確切十進制值,所以你不能相信你看到的。

確切值由下式給出:

(0.014545 asTrueFraction printShowingMaxDecimalPlaces: Float precision - Float emin - 1)
-> '0.01454500000000000063671290462252727593295276165008544921875'

(0.018045 asTrueFraction printShowingMaxDecimalPlaces: Float precision - Float emin - 1)
-> '0.018044999999999998541166945642544305883347988128662109375'

此外,乘以 100 可能會改變舍入...這是因為確切的結果可能不一定可以表示為浮點數:

在 Squeak 中,這兩個表達式的計算結果為 false:

((Float readFrom: '0.014545' readStream) asTrueFraction * 100) isAnExactFloat.
((Float readFrom: '0.018045' readStream) asTrueFraction * 100) isAnExactFloat.

這意味着將再次四舍五入到最接近的可表示浮點數。 幸運的是,乘法確實保留了我們上面介紹的兩個不等式:

(Float readFrom: '0.014545' readStream) * 100 > (Fraction readFrom: '1.4545' readStream).
(Float readFrom: '0.018045' readStream) * 100 < (Fraction readFrom: '1.8045' readStream).

但不一定是這樣:

((014545 to: 018045 by: 10) detect: [:x | (x/1000000) asFloat < (x/1000000) and: [(x/1000000) asFloat * 100 > (x/1000000 * 100)]])
-> 14575

您會看到乘以 100 會改變明顯的舍入方向:

(Float readFrom: '0.014575') < (Fraction readFrom: '0.014575').
(Float readFrom: '0.014575') * 100 > (Fraction readFrom: '1.4575').

(0.014575 asTrueFraction printShowingMaxDecimalPlaces: Float precision - Float emin - 1)
-> '0.01457499999999999941435735451022992492653429508209228515625'

((0.014575*100) asTrueFraction printShowingMaxDecimalPlaces: Float precision - Float emin - 1)
-> '1.457500000000000017763568394002504646778106689453125'

因此,您也不能依賴乘以 100...

此外,您沒有告訴您如何四舍五入到小數點后三位...
介紹中顯示的舍入操作也不精確,浮點引入了一些棘手的邊緣情況。
例如我們有:

0.5 predecessor < 0.5.

因此,我們期望0.5 predecessor rounded = 0 ,幸運的是,這在 Squeak 中是正確的。
但我們有:

(0.5 predecessor + 0.5) truncated = 1.

不是(總是)正確實現舍入...

如果您的 Smalltalk 方言提供某種小數而不是浮點數(Squeak 和 Pharo 有 ScaledDecimal),我建議堅持使用這些小數。
例如你可以寫:

((0.014545s * 100) printShowingDecimalPlaces: 3) , '%'.
((0.018045s * 100) printShowingDecimalPlaces: 3) , '%'.

如果你真的想實現對浮點數的操作,你可以嘗試盡早將浮點數轉換為分數,這樣就可以堅持精確的算術,不引入額外的舍入:

^(self asTrueFraction * 100 printShowingDecimalPlaces: 3) , '%'

在這種情況下,你會得到'1.804%' ,這是公平的......
上面的實現不是特別快,但是很簡單。

如果你想要一些非常棘手的東西,你可以嘗試:

"re-interpret self printed representation as a Fraction.
This may alter the exact value and rounding"
^((Fraction readFromString: self printString) * 100 printShowingDecimalPlaces: 3) , '%'

在其他可能包含虛假的不精確舍入運算的方言中,為 Float 正確實現算法可能很困難!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM