round使用時の合計100%調整に頭を捻る巻

ガチャの出現率があまりにもあまりにも…なので、実際の出現率は何%なのか一覧表示を求められる世の中である。
※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() 」エラーが起きた - 動かざることバグの如し

コメント

タイトルとURLをコピーしました