割引後の価格と税込

元の価格と割引率から、割引後の価格・割引額・税込を出す。

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

言語ごとの結果

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

PHPPHP 8.4.26

よくある書き方10 / 15

直した書き方15 / 15

PythonPython 3.14.7

よくある書き方6 / 15

直した書き方15 / 15

GoGo 1.27.2

よくある書き方10 / 15

直した書き方15 / 15

JavaScriptNode 24.15.0

よくある書き方10 / 15

直した書き方15 / 15

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

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

ケース入力正解 PHPPythonGoJavaScript
c001基本・3000円の20%引き 元ツールの初期値
price
3000
rate
20
tax
0
discounted
2400
off
600
with_tax
2400
✓✓✓✓
c002基本・税込10%
price
3000
rate
20
tax
10
discounted
2400
off
600
with_tax
2640
✓✓✓✓
c00330%引きで .5 ちょうど 45 × 0.7 = 31.5 → 32。浮動小数点では 31.499… になり 31
price
45
rate
30
tax
0
discounted
32
off
13
with_tax
32
discounted31off14with_tax31discounted31off14with_tax31discounted31off14with_tax31discounted31off14with_tax31
c00430%引きで .5 ちょうど(その2) 85 × 0.7 = 59.5 → 60。浮動小数点では 59.499…
price
85
rate
30
tax
0
discounted
60
off
25
with_tax
60
discounted59off26with_tax59discounted59off26with_tax59discounted59off26with_tax59discounted59off26with_tax59
c00533%引きで .5 ちょうど 150 × 0.67 = 100.5 → 101。浮動小数点では 100.499…
price
150
rate
33
tax
0
discounted
101
off
49
with_tax
101
discounted100off50with_tax100discounted100off50with_tax100discounted100off50with_tax100discounted100off50with_tax100
c00650%引きで .5 ちょうど(2進で正確) 502.5 → 503。偶数丸めだと 502。割引額は 1005 − 503 = 502(元ツールは 503 と出す)
price
1005
rate
50
tax
0
discounted
503
off
502
with_tax
503
✓discounted502off503with_tax502✓✓
c00730%引きで 10.5 10.5 → 11。偶数丸めだと 10
price
15
rate
30
tax
0
discounted
11
off
4
with_tax
11
✓discounted10off5with_tax10✓✓
c00890%引きで 0.5 5 × 0.1 = 0.5 → 1。浮動小数点では 0.4999… になり 0
price
5
rate
90
tax
0
discounted
1
off
4
with_tax
1
discounted0off5with_tax0discounted0off5with_tax0discounted0off5with_tax0discounted0off5with_tax0
c009割引率0% 境界値
price
1000
rate
0
tax
0
discounted
1000
off
0
with_tax
1000
✓✓✓✓
c010割引率100% 境界値。税込も 0
price
1000
rate
100
tax
10
discounted
0
off
1000
with_tax
0
✓✓✓✓
c011小数の割引率 666.999… ではなく 667
price
1000
rate
33.3
tax
0
discounted
667
off
333
with_tax
667
✓✓✓✓
c012小数の割引率で .5 ちょうど 1500 × 0.667 = 1000.5 → 1001。偶数丸めだと 1000
price
1500
rate
33.3
tax
0
discounted
1001
off
499
with_tax
1001
✓discounted1000off500with_tax1000✓✓
c013税込で .5 ちょうど 15 × 1.1 = 16.5 → 17。偶数丸めだと 16
price
15
rate
0
tax
10
discounted
15
off
0
with_tax
17
✓with_tax16✓✓
c014軽減税率8% 1048.9 → 1049、1049 × 1.08 = 1132.92 → 1133
price
1234
rate
15
tax
8
discounted
1049
off
185
with_tax
1133
✓✓✓✓
c0152^53 を超える価格 倍精度浮動小数点で正確に表せない整数(2^53+1)。半額は 4503599627370496.5 → 4503599627370497
price
9007199254740993
rate
50
tax
0
discounted
4503599627370497
off
4503599627370496
with_tax
4503599627370497
discounted4503599627370496off4503599627370497with_tax4503599627370496discounted4503599627370496off4503599627370497with_tax4503599627370496discounted4503599627370496off4503599627370497with_tax4503599627370496discounted4503599627370496with_tax4503599627370496

コード

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

Discount.php+13 −4よくある書き方(04ec732)→ 直した書き方

@@ 変更前 1行目〜 / 変更後 1行目〜 @@
11 <?php
22 declare(strict_types=1);
33
4+use BcMath\Number;
5+
46 /**
57 * 割引後の価格と税込。
6− * v1: 素直に書いた版(float と round)。
7− * @return array{discounted:int, off:int, with_tax:int}
8+ * v2: 十進(BcMath\Number)で計算し、四捨五入は RoundingMode::HalfAwayFromZero で明示する。
9+ * (float と round だと 45 × 0.7 が 31.499… になるうえ、PHP 8.4.4 の round は 2^52 以上の偶数を +1 する)
810 */
11+function roundYen(Number $x): int
12+{
13+ return (int) $x->round(0, RoundingMode::HalfAwayFromZero)->value;
14+}
15+
16+/** @return array{discounted:int, off:int, with_tax:int} */
917 function calcDiscount(int $price, string $rate, string $tax): array
1018 {
11− $discounted = (int) round($price * (1 - (float) $rate / 100));
19+ $hundred = new Number(100);
20+ $discounted = roundYen(new Number($price) * ($hundred - new Number($rate)) / $hundred);
1221 return [
1322 'discounted' => $discounted,
1423 'off' => $price - $discounted,
15− 'with_tax' => (int) round($discounted * (1 + (float) $tax / 100)),
24+ 'with_tax' => roundYen(new Number($discounted) * ($hundred + new Number($tax)) / $hundred),
1625 ];
1726 }

仕様

入力

項目型説明
price整数(円)元の価格。0 以上
rate十進文字列割引率(%)。0 以上 100 以下。"33.3" のような小数を含む
tax十進文字列税率(%)。"0"(税込を出さない)・"8"・"10"

出力

項目型定義
discounted整数(円)四捨五入(price × (1 − rate/100))
off整数(円)price − discounted(割引額)
with_tax整数(円)四捨五入(discounted × (1 + tax/100))。tax = "0" なら discounted と同じ
  • 計算は正確な十進(または有理数)で行う。 2進浮動小数点の誤差を結果に出さない
  • 四捨五入は 0.5 ちょうどを繰り上げる(偶数丸めではない)。値はすべて 0 以上
  • price は 64bit 符号付き整数に収まる範囲(c015 は 2^53 を超える値)

元ツールとの違い(2026-10-10 に決めた)

元ツールは次のように計算している。

discounted = price × (1 − rate/100)          … 丸めずに保持し、表示時に Math.round
off        = price − discounted              … 同上
withTax    = discounted × (1 + tax/100)      … 丸める前の discounted に税をかけ、表示時に Math.round

本 spec はこれを2点変えた。

  1. 割引額は「元の価格 − 丸めた割引後の価格」にする。 元ツールは割引後と割引額を別々に丸めるため、 1,005円の50%引きで「割引後 503円・割引額 503円」となり、足すと元の価格を1円超える
  2. 税は丸めた割引後の価格(実際に払う税抜の金額)にかける。 元ツールは丸める前の値にかけている

この2点は浮動小数点の誤差とは別の問題(定義の問題)なので、元ツールに対する指摘として別に扱う。