ポイント還元と実質割引率

支払金額と還元率から、獲得ポイント・実質負担・実質割引率を出す。

2進小数の誤差0.5 の丸め方大きな整数

言語ごとの結果

正解表 13件のうち、正解と一致した件数です。

PHPPHP 8.4.26

よくある書き方9 / 13

直した書き方13 / 13

PythonPython 3.14.7

よくある書き方8 / 13

直した書き方13 / 13

GoGo 1.27.2

よくある書き方8 / 13

直した書き方13 / 13

JavaScriptNode 24.15.0

よくある書き方8 / 13

直した書き方13 / 13

正解表と、よくある書き方の答え

正解は実装より先に決めて固定しています。言語の列の ✓ は正解と一致、赤い値はその言語が実際に返した間違った答えです。

ケース入力正解 PHPPythonGoJavaScript
c001基本・1万円の1%
amount
10000
rate
1
points
100
net
9900
effective_rate
1.0
✓✓✓✓
c0020.7%還元・ちょうど整数 1000 × 0.7% = 7pt ちょうど。浮動小数点では 6.999… になり切り捨てで 6pt
amount
1000
rate
0.7
points
7
net
993
effective_rate
0.7
points6net994effective_rate"0.6"points6net994effective_rate"0.6"points6net994effective_rate"0.6"points6net994effective_rate"0.6"
c0031.4%還元・ちょうど整数 7pt ちょうど。浮動小数点では 6pt
amount
500
rate
1.4
points
7
net
493
effective_rate
1.4
points6net494effective_rate"1.2"points6net494effective_rate"1.2"points6net494effective_rate"1.2"points6net494effective_rate"1.2"
c0045.8%還元・ちょうど整数 58pt ちょうど。浮動小数点では 57pt
amount
1000
rate
5.8
points
58
net
942
effective_rate
5.8
points57net943effective_rate"5.7"points57net943effective_rate"5.7"points57net943effective_rate"5.7"points57net943effective_rate"5.7"
c0055.6%還元・ちょうど整数 7pt ちょうど。浮動小数点では 6pt
amount
125
rate
5.6
points
7
net
118
effective_rate
5.6
points6net119effective_rate"4.8"points6net119effective_rate"4.8"points6net119effective_rate"4.8"points6net119effective_rate"4.8"
c006端数の切り捨て 9.99 → 9pt。実質割引率 0.9009…% → 0.9
amount
999
rate
1
points
9
net
990
effective_rate
0.9
✓✓✓✓
c007実質割引率が .x5 ちょうど 1.2 → 1pt。1/400 = 0.25% → 0.3。偶数丸めだと 0.2
amount
400
rate
0.3
points
1
net
399
effective_rate
0.3
✓effective_rate"0.2"effective_rate"0.2"✓
c008実質割引率 0.5%
amount
400
rate
0.5
points
2
net
398
effective_rate
0.5
✓✓✓✓
c009支払金額0円 境界値。0 で割らない
amount
0
rate
1
points
0
net
0
effective_rate
0.0
✓✓✓✓
c010還元率0% 境界値
amount
1000
rate
0
points
0
net
1000
effective_rate
0.0
✓✓✓✓
c011還元率100% 境界値
amount
1000
rate
100
points
1000
net
0
effective_rate
100.0
✓✓✓✓
c0122^53 を超える金額 倍精度浮動小数点で正確に表せない整数(2^53+1)
amount
9007199254740993
rate
1
points
90071992547409
net
8917127262193584
effective_rate
1.0
✓✓✓net8917127262193583
c013小数2桁の還元率 12.5 → 12pt。実質割引率 1.2
amount
1000
rate
1.25
points
12
net
988
effective_rate
1.2
✓✓✓✓

コード

「違い」では、よくある書き方から直した書き方へ、どの行をどう変えたかを示しています(赤が消した行、緑が足した行、濃い色が行の中で変わった部分)。計算の部分だけを載せ、入出力を受け渡す部分は除いています。

Reward.php+8 −4よくある書き方(f3752ff)→ 直した書き方

@@ 変更前 1行目〜 / 変更後 1行目〜 @@
11 <?php
22 declare(strict_types=1);
33
4+use BcMath\Number;
5+
46 /**
57 * ポイント還元と実質割引率。
6− * v1: 素直に書いた版(float と floor、表示は number_format)。
8+ * v2: 十進(BcMath\Number)で計算し、切り捨て・四捨五入は RoundingMode で明示する。
79 * @return array{points:int, net:int, effective_rate:string}
810 */
911 function calcReward(int $amount, string $rate): array
1012 {
11− $points = (int) floor($amount * ((float) $rate / 100));
12− $eff = $amount > 0 ? $points / $amount * 100 : 0.0;
13− return ['points' => $points, 'net' => $amount - $points, 'effective_rate' => number_format($eff, 1, '.', '')];
13+ $points = (int) (new Number($amount) * new Number($rate) / 100)->round(0, RoundingMode::TowardsZero)->value;
14+ $eff = $amount > 0
15+ ? (new Number($points) * 100)->div($amount, 10)->round(1, RoundingMode::HalfAwayFromZero)->value
16+ : '0.0';
17+ return ['points' => $points, 'net' => $amount - $points, 'effective_rate' => $eff];
1418 }

仕様

入力

項目型説明
amount整数(円)支払金額。0 以上
rate十進文字列還元率(%)。0 以上 100 以下。"0.7"・"1.25" のような小数を含む

出力

項目型定義
points整数(pt)切り捨て(amount × rate / 100)
net整数(円)amount − points(実質負担額。1pt = 1円)
effective_rate十進文字列(%・小数1桁)points / amount × 100 を小数第1位に四捨五入。amount = 0 なら "0.0"
  • 計算は正確な十進(または有理数)で行う。 2進浮動小数点の誤差を結果に出さない
  • 四捨五入は 0.05 ちょうどを繰り上げる(偶数丸めではない)。出力は常に小数1桁("1.0")
  • 元ツールは実質割引率を "1%" のように末尾の 0 を省いて表示する。本 spec は小数1桁固定の数値文字列にした
  • amount は 64bit 符号付き整数に収まる範囲(c012 は 2^53 を超える値)

期待値の作り方

expect は oracle.py で有理数による定義の直訳から生成した(2026-10-10)。実装ではない。runner は使わない。