「銀行丸め」を知っているか?(JavaScript版)
経緯
フロントエンドエンジニアとして、自社サービスのキャッシュレスPOSレジ(タブレットアプリ)をReact Nativeで開発しています。
先日、値引按分額を算出する際にデバッグしていたところ「1円がどうしても合わない!!!」と悩んでいました。
調べたところ、「銀行丸め」で計算すればOKとのこと。
「銀行丸め」とは何か
こちらの記事に詳しく書かれていました。
抜粋すると下記の通り。四捨五入の特殊版です。
「端数が0.5より小さいなら切り捨て、端数が0.5より大きいならば切り上げる。端数がちょうど0.5なら切り捨てと切り上げのうち結果が偶数となる方へ丸める。」
前提
APIで下記のような会計処理するデータを取得したとする。
★ 問題ない会計パターン [ 商品A:{ price:750, }, 商品B:{ price:300, }, 商品C{ price:1180, }, subTotal:2230, //小計 discount:0, //クーポン値引額 total:2230, //最終支払額 ]
しかし、上記のケースで値引クーポン(会計から400円値引する)がある場合はどうだろうか?
仕様により、「商品ごとに値引額を按分し、先に算出しないといけない」とする。
★ 1円ずれる会計パターン 400円を商品A~Cで按分(値引額 * 商品代金 / 小計)し、itemDiscountに代入 [ 商品A:{ price:300, itemDiscount:54, //按分した単品の値引額 }, 商品B:{ price:750, itemDiscount:135, //按分した単品の値引額 }, 商品C{ price:1180, itemDiscount:212, //按分した単品の値引額 }, subTotal:2230,//小計 discount:401,//クーポン値引額(54+135+212=401) total:1829,//最終支払額、正しくは1830 ] discount:401、、、だと!?
ちなみにitemDiscountの算出方法は 値引額 * 商品代金 / 小計 の四捨五入 つまり商品Aなら、 400 * 300 / 2230 = 53.811.... → 四捨五入し「54円」 しかし、商品Bでは 400 * 750/ 2230 = 134.5 → 四捨五入し「135円」となるが、 少数点第一位が.5なので 「銀行丸め」により「134円」としないと1円オーバーしてしまう! 商品B:{ price:750, itemDiscount:135, //1円オーバーの原因 },
答え
const calcItemDiscountWithCoupon = (orderData) => { const apportionmentPrice = 値引額 * 商品代金 / 小計 //按分後、少数点以下が'.5'の場合に銀行丸めを行う if (apportionmentPrice.indexOf('.5') !== -1) { //少数点以下を切捨て2で割りきれるか確認 const floorApportionmentPrice = Math.floor(apportionmentPrice) if(floorApportionmentPrice % 2 === 0){ return floorApportionmentPrice //偶数ならそのまま返す } else{ return Math.ceil(apportionmentPrice)//奇数ならapportionmentPriceを切り上げ } } } これによって、商品Bの値引き額は「134円」になった!!!
この後に「単品でのディスカウントや消費税(8%・10%)」の分岐を踏まえて、
「内税算出(表示義務化が2021年4月施行)」するために必要な工程の一つでしたが、
基本的には上記で「銀行丸め」しました。