余数运算符在Java中用于Doubles
我在OSU任教已近兩年了,這總是令我驚訝,我從學(xué)生那里學(xué)到了多少。 例如,過去, 我讓學(xué)生寫一些我不理解的奇怪代碼 。 在這一點上,即使經(jīng)過300多個博客文章, 幾個YouTube視頻 ,甚至從100多種語言中收集代碼段 ,您都認為我已經(jīng)看完了。 好吧,最近,我看到一個學(xué)生在雙打中使用余數(shù)運算符( % ),從那以后我就再也沒有真正的相同了。
余數(shù)與模運算符
在開始講故事之前,我想先介紹一下余數(shù)運算符和模數(shù)運算符。 在Java中, 沒有模運算符 。 相反, %是余數(shù)運算符。 對于正數(shù),它們在功能上是等效的。 但是,一旦開始使用負數(shù),我們將看到令人驚訝的差異。
我已經(jīng)在有關(guān)RSA加密的文章中談到了這種差異。 就是說,我找到了另一個很好的來源 ,可以比較Java,Python,PHP和C等各種語言中的“模”運算符。
總而言之,余數(shù)運算符的工作原理與我們期望的正數(shù)一樣。 例如,如果我們采用3 % 5 ,我們將得到3,因為5根本就不等于3。 如果我們開始用負數(shù)計算,結(jié)果將是相似的。 例如,如果我們使用3 % -5 ,我們?nèi)匀粫玫? % -5 ,因為這就是剩下的全部。
同時,如果我們翻轉(zhuǎn)腳本并使股息為負(畢竟,余數(shù)是除法的副產(chǎn)品),我們將開始看到負余數(shù)。 例如, -3 % 5返回-3。 同樣, -3 % -5返回-3。
請注意,在所有這些示例中,如何在符號上有所不同的情況下獲得相同的結(jié)果。 換句話說,對于余數(shù)運算符,我們不太關(guān)心符號。 我們只想知道一個數(shù)字變成另一個數(shù)字的次數(shù)。 然后,我們偷看股息以確定征兆。
在另一方面,取模運算符還有很多細微差別。 對于啟動器,右側(cè)的操作數(shù)確定可能的返回值范圍。 如果該值為正,則結(jié)果將為正。 這與我們剩余的運算符有點不同。
同時,左操作數(shù)確定我們在可能值范圍內(nèi)循環(huán)的方向。 自然,當兩個值具有相同的符號時,這與余數(shù)運算符完美匹配。 不幸的是,它們在任何其他情況下都是完全不同的:
| 3 % 5 | 3 | 3 |
| 3 % -5 | 3 | -2 |
| -3 % 5 | -3 | 2 |
| -3 % -5 | -3 | -3 |
如果您有興趣學(xué)習(xí)有關(guān)模塊化算術(shù)的更多信息,另一位學(xué)生啟發(fā)我寫了一篇關(guān)于使用模塊化算術(shù)的Rock Paper Scissors游戲的文章 。
雙打余數(shù)運算符
當我們考慮余數(shù)運算符時,通常會假設(shè)它僅與整數(shù)一起使用-至少直到最近,這才是我的理解。 事實證明,余數(shù)運算符實際上對浮點數(shù)起作用,這是有道理的。
靈感
本月初,我正在與一個學(xué)生一起在實驗室里工作,該實驗室要求他們編寫一個硬幣兌換程序。 具體來說,該程序應(yīng)從用戶那里接受許多美分,并以美國貨幣(例如美元,半美元,四分之一,角錢,鎳幣和便士)輸出面額。
如果您正在考慮如何解決此問題,我會給您一個提示:您可以采用貪婪的方法。 換句話說,首先選擇最大的硬幣,然后計算將其中多少分成您當前的美分。 如果做對了,您甚至不需要控制流程。 但是,您可以使用數(shù)組和循環(huán)來稍微整理一下代碼。 由于我懶得用Java編寫解決方案,因此它在Python中可能看起來像這樣:
3 % 5無論如何,我有一個學(xué)生將美分解釋為美元和美分。 換句話說,他們讓用戶輸入1.5美元(而不是150美分)等美元金額。 公平地說,這不是什么大問題。 我們要做的就是將美元金額乘以100,然后將剩余的美分相加得到一個整數(shù)。
但是,那不是那個學(xué)生所做的。 取而代之的是,他們將每個面額都視為雙倍(即實數(shù))。 然后,他們繼續(xù)使用剩下的運算符而沒有任何后果。 簡單地說,我傻眼了。 畢竟,那怎么可能呢? 您只計算長除法的余數(shù),對嗎? 否則,您將只剩下一個小數(shù),什么也沒剩下-還是我想。
使用雙打
如果要使用美元和美分重寫上面的程序,則可能會出現(xiàn)以下內(nèi)容:
3 % 5如果運行此命令,我們將獲得與以前完全相同的結(jié)果:一美元和一半美元。 那怎么可能?
事實證明,使用小數(shù)來計算余數(shù)是完全有效的。 我們需要做的就是計算將除數(shù)完全除數(shù)的次數(shù)。 例如, .77 % .25將“理想地”產(chǎn)生.02,因為這與我們不經(jīng)過而就達到.77的程度非常接近。
注意事項
在發(fā)現(xiàn)可以取小數(shù)點后的余數(shù)之后,我立即想知道為什么我不早知道它。 當然,快速的Google搜索會向您顯示可能出現(xiàn)的各種錯誤行為。
例如,在前面的示例中,我聲稱.02將是.77和.25的余數(shù),并且有點。 請參閱,在大多數(shù)編程語言中,默認浮點值具有一定的精度,該精度由基礎(chǔ)二進制體系結(jié)構(gòu)決定。 換句話說,有些十進制數(shù)字不能用二進制表示。 這些數(shù)字之一恰好是上述表達式的結(jié)果:
3 % 5在處理實數(shù)時,我們始終會遇到此類問題。 畢竟,有令人驚訝的十進制數(shù)無法用二進制表示。 結(jié)果,我們最終遇到了四舍五入錯誤可能導(dǎo)致更改算法失敗的情況。 為了證明這一點,我重寫了上面的解決方案以計算前200美分的變化:
3 % 5為了您的理智,我不會轉(zhuǎn)儲結(jié)果,但是在該算法失敗的地方,我將分享一些金額:
- $ 0.06(計算鎳時失敗: .06 % .05 )
- $ 0.08(計算便士時失敗: .03 % .01 )
- $ 0.09(計算鎳價時失敗: .09 % .05 )
- $ 0.11(計算點數(shù)時失敗: .11 % .1 )
- $ 0.12(計算點數(shù)時失敗: .12 % .1 )
- $ 0.13(與$ 0.08相同)
- $ 0.15(計算點數(shù)時失敗: .15 % .1 )
- $ 0.16(與$ 0.06相同)
我們已經(jīng)開始看到這些計算中令人震驚的部分成為四舍五入錯誤的犧牲品。 僅在最初的16美分中,我們就無法在50%的時間內(nèi)產(chǎn)生準確的變化(忽略0)。 那不是很好!
另外,許多錯誤開始重演。 換句話說,我懷疑隨著更多的可能性,這個問題會變得更糟,因為在此過程中有更多的機會舍入錯誤。 當然,我繼續(xù)并再次修改了程序以實際測量錯誤率:
3 % 5現(xiàn)在,我應(yīng)該作為序言,此代碼段使用==比較實數(shù),這通常被認為是不好的做法。 結(jié)果,有可能我們認為一些“正確”的解決方案是錯誤的。 就是說,我認為目前這是一個足夠好的估計。
當我運行它時,我發(fā)現(xiàn)所有更改計算中的53.850699999999996%不正確。 具有諷刺意味的是,即使是我的錯誤計算也存在一個舍入問題。
您是否應(yīng)該在雙打中使用余數(shù)運算符?
在這一點上,我們不得不懷疑在Java中對double使用其余運算符是否有意義。 畢竟,如果舍入錯誤是一個問題,誰能相信結(jié)果呢?
就我個人而言,我的直覺會說不惜一切代價避免執(zhí)行此操作。 就是說,我做了一些挖掘,并且有幾種方法可以解決此問題。 例如,我們可以嘗試使用一個將浮點值表示為整數(shù)字符串的類(例如Python中的Decimal類或Java中的BigDecimal類)在另一個基礎(chǔ)上執(zhí)行算術(shù)。
當然,這些類型的類都有其自身的性能問題,并且無法擺脫以10為底的舍入錯誤。畢竟,以10為底的值不能代表三分之一。 也就是說,使用余數(shù)運算符將獲得更多成功。
然而,歸根結(jié)底,我個人還沒有遇到這種情況,并且我懷疑您也會這樣做。 當然,如果您在這里,很可能是因為遇到了這個確切的問題。 不幸的是,我沒有為您提供很多解決方案。
無論如何,謝謝您的光臨。 如果您發(fā)現(xiàn)本文有趣,請考慮分享。
翻譯自: https://www.javacodegeeks.com/2020/02/the-remainder-operator-works-on-doubles-in-java.html
總結(jié)
以上是生活随笔為你收集整理的余数运算符在Java中用于Doubles的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 备案的合同(备案合同款)
- 下一篇: HelloWorld.java –打印H