| | | @@ 変更前 1行目〜 / 変更後 1行目〜 @@ |
| 1 | 1 | | package main |
| 2 | 2 | | |
| 3 | 3 | | import ( |
| 4 | | − | "math" |
| 5 | | − | "strconv" |
| 4 | + | "fmt" |
| 5 | + | "math/big" |
| 6 | 6 | | ) |
| 7 | 7 | | |
| 8 | 8 | | // Input は単価比較の入力。 |
| | | @@ 変更前 14行目〜 / 変更後 14行目〜 @@ |
| 14 | 14 | | BQty string `json:"b_qty"` |
| 15 | 15 | | } |
| 16 | 16 | | |
| 17 | | − | func num(s string) float64 { v, _ := strconv.ParseFloat(s, 64); return v } |
| 17 | + | // tenths は price ÷ qty × base を 0.1 単位に四捨五入した整数を返す(0.05 ちょうどは繰り上げ)。 |
| 18 | + | func tenths(price, qty string, base int64) (int64, error) { |
| 19 | + | p, ok1 := new(big.Rat).SetString(price) |
| 20 | + | q, ok2 := new(big.Rat).SetString(qty) |
| 21 | + | if !ok1 || !ok2 { |
| 22 | + | return 0, fmt.Errorf("invalid decimal: %q / %q", price, qty) |
| 23 | + | } |
| 24 | + | v := new(big.Rat).Quo(p, q) |
| 25 | + | v.Mul(v, big.NewRat(base*10, 1)) |
| 26 | + | v.Add(v, big.NewRat(1, 2)) |
| 27 | + | return new(big.Int).Quo(v.Num(), v.Denom()).Int64(), nil |
| 28 | + | } |
| 29 | + | |
| 30 | + | func fmt1(t int64) string { return fmt.Sprintf("%d.%d", t/10, t%10) } |
| 18 | 31 | | |
| 19 | 32 | | // Compare はどちらがお得かを判定する。 |
| 20 | | − | // v1: よくある書き方(float64 で単価を出し、×10 して math.Round で 0.1 単位にそろえて比べる)。 |
| 33 | + | // v2: math/big.Rat で単価を正確に出し、0.1 単位への四捨五入も整数で行う |
| 34 | + | // (float64 だと 97 ÷ 80 × 100 が 121.24999… になり、本当は同じ単価を「A がお得」と判定してしまう)。 |
| 21 | 35 | | func Compare(in Input) (map[string]string, error) { |
| 22 | | − | base := 100.0 |
| 36 | + | base := int64(100) |
| 23 | 37 | | if in.Unit == "個" { |
| 24 | 38 | | base = 1 |
| 25 | 39 | | } |
| 26 | | − | ta := int64(math.Round(num(in.APrice) / num(in.AQty) * base * 10)) |
| 27 | | − | tb := int64(math.Round(num(in.BPrice) / num(in.BQty) * base * 10)) |
| 28 | | − | verdict := "same" |
| 40 | + | ta, err := tenths(in.APrice, in.AQty, base) |
| 41 | + | if err != nil { |
| 42 | + | return nil, err |
| 43 | + | } |
| 44 | + | tb, err := tenths(in.BPrice, in.BQty, base) |
| 45 | + | if err != nil { |
| 46 | + | return nil, err |
| 47 | + | } |
| 48 | + | verdict, d := "same", ta-tb |
| 29 | 49 | | if ta < tb { |
| 30 | | − | verdict = "A" |
| 50 | + | verdict, d = "A", tb-ta |
| 31 | 51 | | } else if ta > tb { |
| 32 | 52 | | verdict = "B" |
| 33 | 53 | | } |
| 34 | | − | f := func(t int64) string { return strconv.FormatFloat(float64(t)/10, 'f', 1, 64) } |
| 35 | | − | d := ta - tb |
| 36 | | − | if d < 0 { |
| 37 | | − | d = -d |
| 38 | | − | } |
| 39 | | − | return map[string]string{"unit_a": f(ta), "unit_b": f(tb), "verdict": verdict, "diff": f(d)}, nil |
| 54 | + | return map[string]string{"unit_a": fmt1(ta), "unit_b": fmt1(tb), "verdict": verdict, "diff": fmt1(d)}, nil |
| 40 | 55 | | } |