LoginSignup
12
7

More than 5 years have passed since last update.

AGC019のAでコケました。

問題としては、

0.25リットルのジュースはQ円、
0.5リットルのジュースはH円、
1リットルのジュースはS円、
2リットルのジュースはD円。

ぴったりNリットルのジュースが欲しい場合、
一番安い組み合わせで買ったらいくらになりますか?(意訳)
ただし、Q、H、S、D、Nはすべて整数とする。

というもの。

解法としては、Nは整数なので、1リットルで一番安いものを計算します。具体的には

LITTER_1 = min(Q * 4, H * 2, S)

こんな感じ。
で、後は、2リットルと1リットルの単価を比較して計算する、という流れなんですけれども。

その中で、なぜか通らないコードの一部を抜粋したのが、以下です。

<?php

$litter1 = 99999995;
$litter2 = 99999997;
$question = 999999999;

// 1リッターが2つの方が高くつく場合
if ($litter1 * 2 > $litter2) {
    // 問題が偶数の場合
    if ($question % 2 == 0) {
        printf("%d\n", $question / 2 * $litter2);
    } else {
        printf("%d\n", floor($question / 2) * $litter2 + $litter1);
    }
} else {
    printf("%d\n", $question * $litter1);
}

正解は 49999998499999998 なんですが……

image.png

実行してみると、なんか 6 ずれてるんです。

image.png

なんでだろう、と思って、細かく見てみて、唖然。

$step1 = floor($question / 2);
printf("[1]%d\n", $step1);
printf("[2]%s\n", gettype($step1));
$step2 = $step1 * $litter2;
printf("[3]%d\n", $step2);
printf("%d\n", floor($question / 2) * $litter2 + $litter1);

image.png

999999999 を 2 で割って、床関数で切り捨てて、 499999999 を得ています。[1]
これはOK。
そこに 99999997 を掛けて…… [3]を見ると、明らかにおかしい。499999999 × 99999997 で最後にこんなに 0 が並ぶことは、ありえない。

[1]の時点での変数の型を調べてみると、doubleになってます。[2]

この時点で、何かちょっとおかしい。床関数を使って切り捨てている。整数を得ているはずなのに、なぜ double なのか。
マニュアルを見ると、
value をこえない最大の整数の値を (float 型で) 返します。
と書いてあります。
なぜfloat型なのか。
これは、 float の範囲が int よりも広いためです。
……???
ちょっと何を言ってるのか分かりませんが、浮動小数点の形で返されているせいで、正しい数を得ることができていません。

(int)でキャストしても良いですが、とりあえず整数型で返す関数を定義します。

function ifloor($value) {
    return intval($value);
}

こうすることによって、正しい値を得ることができました。

image.png

結論

floorceilを使うときは、いちいちintにキャストした方が良いよ。

いやぁ、こんな罠が潜んでいるとは……

12
7
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
7