您好,登錄后才能下訂單哦!
金錢無處不在。
無論在銀行應(yīng)用程序、電子商務(wù)網(wǎng)站還是證券交易所平臺(tái),我們每天都在與金錢互動(dòng)。我們也越來越依賴技術(shù)來處理問題。
然而,關(guān)于如何以編程處理貨幣價(jià)值尚無共識(shí)。雖然金錢是現(xiàn)代社會(huì)中普遍存在的概念,但相較于日期和時(shí)間之類的東西,它并不是任何主流語言中的一流數(shù)據(jù)類型。結(jié)果,每一種軟件都有自己的處理方式,且伴隨著陷阱。
陷阱#1:金錢是數(shù)字?
當(dāng)你需要代表錢時(shí),你的第一直覺可能是使用一個(gè)數(shù)字。
金錢只不過是一個(gè)數(shù)值,對吧?
錯(cuò)了。
貨幣價(jià)值的一部分與另一對象有關(guān):貨幣。沒有10“錢”,應(yīng)該是“10美元,10歐元,10比特幣”......如果你想用不同的貨幣添加兩個(gè)貨幣值,你需要先轉(zhuǎn)換它們。如果你想比較它們也是如此:如果你只有一個(gè)金額,你就無法進(jìn)行準(zhǔn)確的比較。金額和貨幣誰也離不開誰。
陷阱#2:讓人煩惱的小數(shù)點(diǎn)
大多數(shù)現(xiàn)代貨幣額都是以小數(shù)形式出現(xiàn),或根本沒有子單位。這意味著當(dāng)貨幣有子單位時(shí),主單位中這些單位的數(shù)量是10的冪。例如,一美元有100美分——10的2次冪。
使用十進(jìn)制系統(tǒng)具有優(yōu)勢,但在編程方面有一個(gè)問題。計(jì)算機(jī)使用二進(jìn)制系統(tǒng),因此它們不能原生地表示十進(jìn)制數(shù)。有些語言提出了自己的解決方案,如Java中的BigDecimal類型或C#中的小數(shù)類型。JavaScript只有Number類型,可以用作整數(shù)或雙精度浮點(diǎn)數(shù)。因?yàn)樗腔A(chǔ)10系統(tǒng)的二進(jìn)制表示,所以當(dāng)你嘗試進(jìn)行數(shù)學(xué)運(yùn)算時(shí),最終會(huì)得到不準(zhǔn)確的結(jié)果。
使用浮點(diǎn)來存儲(chǔ)貨幣價(jià)值是一個(gè)壞主意。
當(dāng)你計(jì)算更多值時(shí),難以察覺的精度誤差會(huì)導(dǎo)致更大的差異。這不可避免地導(dǎo)致最終的四舍五入問題。
陷阱#3:百分比與分期
有時(shí)你需要計(jì)算分期,但百分比總是或增加或減少付款數(shù)。
想象一下,你需要支付999.99美元,首付50%。這可以通過一些簡單的數(shù)學(xué)來完成。一半是499.995美元,但是你不能再分一分錢,所以你可能會(huì)把結(jié)果分成500美元。問題是,當(dāng)你付尾款時(shí),你最終會(huì)獲得相同的結(jié)果并且額外付一分錢。
分期有時(shí)候會(huì)遇到除不盡的情況。天然氣價(jià)格可能顯示超過兩位數(shù),但它只是象征性的:你最終總是支付一個(gè)四舍五入的價(jià)格。
編程人員應(yīng)該怎么辦?
幸運(yùn)的是,軟件工程師Martin Fowler提出了一個(gè)解決方案。在企業(yè)應(yīng)用程序架構(gòu)模式中,他描述了貨幣價(jià)值的模式:
屬性
方法
數(shù)學(xué) :加,減,乘,除
比較 :等于,大于,大于或等于,小于,小于或等于。
由此,你可以創(chuàng)建滿足大部分貨幣需求的價(jià)值對象。
“金額+貨幣”作為數(shù)據(jù)結(jié)構(gòu)
金錢的行為與簡單的數(shù)字不同,因此應(yīng)區(qū)別對待。第一個(gè)也是最重要的是:它應(yīng)該始終由金額和貨幣組成。
你可以將貨幣金額一起添加,檢查它們的值是否相對應(yīng),并將它們格式化為你需要的任何內(nèi)容。這可以通過對象的方法完成。在JavaScript中,任何返回對象的函數(shù)都可以解決問題。
以美分計(jì)額
有幾種方法可以解決JavaScript中的浮點(diǎn)問題。
你可以使用像Decimal.js這樣的庫來將浮點(diǎn)數(shù)作為字符串。這不是一個(gè)糟糕的解決方案,當(dāng)你必須處理大數(shù)字時(shí),它會(huì)派上用場。然而,它以增重依賴性為代價(jià),導(dǎo)致性能降低。
你可以在計(jì)算之前將浮點(diǎn)數(shù)乘以整數(shù),然后將它們分開。
這是一個(gè)很好的解決方案,但需要在對象構(gòu)造或每次操作時(shí)進(jìn)行額外的計(jì)算。這不一定會(huì)影響性能,但仍然需要更多的流程工作。
第三種方法是直接以美分為單位存儲(chǔ)相對于單位的值。如果你需要存儲(chǔ)10美分,則不會(huì)存儲(chǔ)0.1,而是10.這允許你僅使用整數(shù),這意味著安全計(jì)算(直到你遇到大數(shù)字)和出色的性能。
如何處理JavaScript 中的貨幣值
Dinero.js:一個(gè)用于創(chuàng)建、計(jì)算和格式化貨幣價(jià)值的不可變庫
從以上觀察中,我創(chuàng)建了一個(gè)JavaScript庫:Dinero.js。
Dinero.js遵循Fowler的模式更多一點(diǎn)兒。它允許你在JavaScript中創(chuàng)建、計(jì)算和格式化貨幣值。你可以進(jìn)行數(shù)學(xué)運(yùn)算、解析和格式化對象,甚至向他們提問,使你的開發(fā)過程更加輕松。
該庫設(shè)計(jì)為不可變和可鏈接的模式。它支持全局設(shè)置,具有擴(kuò)展格式選項(xiàng),并提供本機(jī)國際化支持。
為什么不可變?
不可變庫更安全,更好預(yù)測。可變操作和引用副本是許多錯(cuò)誤的來源。選擇不變性能夠避免了這些錯(cuò)誤。
使用Dinero.js,你可以執(zhí)行計(jì)算而無需擔(dān)心更改原始用例。在以下Vue.js示例中,調(diào)用priceWithTax時(shí)不會(huì)更改Price。如果用例是可變的,它將會(huì)更改價(jià)格。
可鏈接性
優(yōu)秀的開發(fā)人員努力使他們的代碼更簡潔,更易于閱讀。當(dāng)你想要在單個(gè)對象上連續(xù)執(zhí)行多個(gè)操作時(shí),鏈接提供了優(yōu)雅的符號(hào)和簡潔的語法。
全球設(shè)置
當(dāng)你處理大量貨幣價(jià)值時(shí),你可能希望其中一些人分享一些屬性。如果你使用德語制作網(wǎng)站,你可能希望以德國貨幣格式顯示金額。
這是全球設(shè)置派上用場的地方。你可以聲明將應(yīng)用于所有新對象的選項(xiàng),而不是將它們傳遞給每個(gè)用例。
原生國際化支持
傳統(tǒng)意義上,庫使用區(qū)域設(shè)置文件進(jìn)行國際化。
區(qū)域設(shè)置文件也很難維護(hù)。Internationalization API是原生的,并且得到了很不錯(cuò)的支持。除非你必須使用過時(shí)的或不知名的瀏覽器,否則toFormat可以安全使用。
格式化
對象很適合存儲(chǔ)數(shù)據(jù),但在顯示數(shù)據(jù)時(shí)卻沒那么有用。Dinero.js提供了各種格式化方法,包括toFormat。它為Number.prototype.toLocaleString提供了直觀而簡潔的語法。將它與setLocale配對,你將能夠以任何語言將任何Dinero對象顯示為正確的格式。這對多語言電子商務(wù)網(wǎng)站特別有用。
接下來做什么?
大家廣泛認(rèn)同F(xiàn)owler模式是一個(gè)不錯(cuò)的解決方案。它激發(fā)了許多語言的同步實(shí)現(xiàn)。如果你正在DIY,我推薦它和本文的觀察結(jié)果作為起點(diǎn)?;蛘吣憧梢赃x擇Dinero.js:一種現(xiàn)代,可靠,經(jīng)過全面測試的解決方案,已經(jīng)可以使用。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。