ガチャの出現率があまりにもあまりにも…なので、実際の出現率は何%なのか一覧表示を求められる世の中である。
※2017年、Appleでガチャ課金の確率表示を義務化!
そこで『確率(%)を一覧表示公開』をするわけだが、
小数点が長くなりすぎると見づらいので『小数点を第X位を切り捨てる(round)』。
すると『表示された値の合計が100にならない』という問題が出た。(丸め誤差)
まぁ、当然である。
この際『1/100単位の差異は良いので、合計100にする』を求められた。
そこで『加工された値(round済)を足していき、出た差分を最終値に足し100にする』ということになった。
そこで何とかしたいと考えた自分の右往左往メモ。
合計100になる配列を作るも何かオカシイ
まず、状況を似せて配列の値を全て足して100になる配列を作ろうと考えた。
ので、下記のように桁数の荒れた配列の4要素を作成し、差分を配列に追加しようと考えた。
$array = [ // 合計する値が入る配列 31.4159265358979323846264338, (10/3), 45.676543210123456789, 0.987654321, ]; $sum = 0; foreach($array as $key => $val){$sum+= $val;} $diff = 100-$sum; $array[] = $diff; print($diff);
100にするために必要な差分($diff)は『18.586542599645』
ん?おかしい…これ合計100になるわけがなくない?
そこで配列の中身を表示(var_dump)してみると
array (size=5) 0 => float 31.415926535898 1 => float 3.3333333333333 2 => float 45.676543210123 3 => float 0.987654321 4 => float 18.586542599645
何故こうなるかというと、PHPの仕様で桁数限界があるということ。
そりゃあ無理なわけだよ、どうしようもないよ!
1枚1枚計算してたら確率0.0000000000001以下とか余裕であるよ!
桁上げしてINT化で計算とか考えたけど、そもそも計算する前に追い出されてるわけだよ!
とりあえずそれは置いておくことにした…良くないけども。
小数点2桁で切上げ計算の差異を誤魔化す
元々の目的だった小数点2桁で切上げ計算の差異確認してみる
通常
31.415926535898+3.3333333333333+45.676543210123+0.987654321+18.586542599645=100
下2桁で四捨五入:round(xx,2)
31.42+3.33+45.68+0.99+18.59=100.01
うん、100を超えた。四捨五入だもんね!
さて100との差分を計算してみる…と『-0.010000000000005』
おかしな数値が出ている…。
これはPHPの関数で丸めると、PHP内で丸める前の値を保存してあるので、実際に表示されている値との加算結果が変わるから…らしい…。
※ネット情報だが、あちこち行ってる間に参照元見失った…
ということで、何とか誤魔化す方法を考えた結果がこちら。
$scale = 2; // 小数点下2桁 $sum = 0; // 合計値 // 合計する foreach($array as $key => $val){ $array[$key] = round($val,$scale); $sum = bcadd($sum, $array[$key], $scale); // 加算 } // 差分を最後の値に加算 $diff = bcsub(100,$sum,$scale); // 減算 if($diff != 0){ end($array); $key = key($array); $array[$key] = bcadd($array[$key], $diff, $scale); // 誤差を加算 }
結果:
31.42+3.33+45.68+0.99+18.58=100
多分、これでイケる…はず!!
(strong)して(float)してみたりしたけど、元通りになるし、
桁上げして(INT)化して計算して桁下げしたけど当時上手くいかなかったんだよな…
多分やっぱり加工前の値を保存しているせいなんだよ…
BCMath関数はサーバに使うための設定が必要なので注意!
参照:PHPで「Call to undefined function bcadd() 」エラーが起きた - 動かざることバグの如し
コメント