[英]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.014545
( Datatype: 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 方言及其版本,不保證:
至少,在最近的 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.