[PHP]fmod関数徹底解説 – 浮動小数点数の剰余計算マスターガイド

PHP

PHPで浮動小数点数の剰余(余り)を計算するための重要な関数であるfmod()について、基本から応用まで詳しく解説します。

fmod関数の基本

fmod()関数は、2つの数値の剰余(余り)を計算します。特に浮動小数点数に対応している点が通常の%演算子と異なります。

基本構文

float fmod ( float $x , float $y )
  • $x: 被除数(割られる数)
  • $y: 除数(割る数)
  • 戻り値: $x$y で割った余り(浮動小数点数)

基本的な使用例

<?php
echo fmod(5.7, 1.3);    // 出力: 0.5
echo "\n";
echo 5.7 % 1.3;         // 通常の%演算子では警告が出る可能性あり

echo "\n";
echo fmod(10.5, 2.5);   // 出力: 0.5
echo "\n";
echo fmod(-10.5, 2.5);  // 出力: -0.5 (符号は被除数に依存)

%演算子との違い

PHP の % 演算子は主に整数に対して使用されます。浮動小数点数に使うとPHPバージョンによっては警告が出たり、整数にキャストされることがあります。

<?php
// %演算子とfmodの比較
$a = 10.5;
$b = 3.2;

// fmod - 浮動小数点数のまま計算
$result1 = fmod($a, $b);
echo "fmod($a, $b) = $result1\n";  // 出力: fmod(10.5, 3.2) = 0.9

// %演算子 - 整数にキャストされる可能性あり
$result2 = $a % $b;
echo "$a % $b = $result2\n";       // PHPバージョンによって結果が異なる

実践的な使用例

例1: 周期的な値の計算

<?php
// 時計の針の角度計算
function clock_angle($hours, $minutes) {
    // 時針の角度: 1時間 = 30度, 1分 = 0.5度
    $hour_angle = ($hours % 12) * 30 + $minutes * 0.5;

    // 分針の角度: 1分 = 6度
    $minute_angle = $minutes * 6;

    // 針の間の角度差
    $angle = abs($hour_angle - $minute_angle);

    // 最小角を返す(180度以下)
    return min($angle, 360 - $angle);
}

// 3時15分の時計の針の角度
$angle = clock_angle(3, 15);
echo "3時15分の時針と分針の角度差: {$angle}度\n";

例2: 循環パターンの生成

<?php
// 日付に基づく循環パターンの生成
function get_rotation_pattern($date, $patterns) {
    $timestamp = strtotime($date);
    $days_since_epoch = floor($timestamp / 86400); // 1970年からの日数

    $pattern_count = count($patterns);
    $index = (int)fmod($days_since_epoch, $pattern_count);

    return $patterns[$index];
}

// 3パターンのローテーション
$patterns = ['A班', 'B班', 'C班'];
$today = date('Y-m-d');
$assigned_team = get_rotation_pattern($today, $patterns);

echo "今日({$today})の担当: {$assigned_team}\n";

例3: 金融計算での端数処理

<?php
// 端数を特定の単位に合わせる(例: 10円単位)
function round_to_nearest($amount, $unit) {
    $remainder = fmod($amount, $unit);

    if ($remainder >= $unit / 2) {
        return $amount + ($unit - $remainder);
    } else {
        return $amount - $remainder;
    }
}

$price = 1234.56;
echo "元の価格: {$price}円\n";
echo "10円単位に四捨五入: " . round_to_nearest($price, 10) . "円\n";
echo "100円単位に四捨五入: " . round_to_nearest($price, 100) . "円\n";

特殊なケースと注意点

負の数での動作

<?php
echo "正の数の例:\n";
echo "fmod(7.5, 2.5) = " . fmod(7.5, 2.5) . "\n";    // 出力: 0
echo "fmod(7.5, 2.0) = " . fmod(7.5, 2.0) . "\n";    // 出力: 1.5

echo "負の数の例:\n";
echo "fmod(-7.5, 2.5) = " . fmod(-7.5, 2.5) . "\n";  // 出力: -0
echo "fmod(-7.5, 2.0) = " . fmod(-7.5, 2.0) . "\n";  // 出力: -1.5
echo "fmod(7.5, -2.5) = " . fmod(7.5, -2.5) . "\n";  // 出力: 0
echo "fmod(-7.5, -2.5) = " . fmod(-7.5, -2.5) . "\n";// 出力: -0

fmod()の結果の符号は常に被除数(最初のパラメータ)の符号に従います。

ゼロ除算の扱い

<?php
// ゼロ除算
try {
    $result = fmod(10.5, 0);
    echo "結果: $result\n";
} catch (DivisionByZeroError $e) {
    echo "エラー: " . $e->getMessage() . "\n";
}

PHP 7以降では、ゼロで除算するとDivisionByZeroError例外が発生します。

浮動小数点数の精度問題

<?php
// 浮動小数点数の精度問題
$result = fmod(0.1 + 0.7, 0.1);
echo "fmod(0.1 + 0.7, 0.1) = $result\n";  // 期待: 0、実際: 小さな値

// より安全な比較方法
function is_multiple_of($number, $divisor, $epsilon = 0.0000001) {
    return abs(fmod($number, $divisor)) < $epsilon ||
           abs(fmod($number, $divisor) - $divisor) < $epsilon;
}

if (is_multiple_of(0.1 + 0.7, 0.1)) {
    echo "0.8は0.1の倍数です\n";
} else {
    echo "0.8は0.1の倍数ではありません\n";
}

数学的な応用例

例4: 数学的周期性の実装

<?php
// 角度を0-360度の範囲に正規化
function normalize_angle($angle) {
    return fmod($angle, 360.0) + ($angle < 0 ? 360 : 0);
}

// 異なる角度の正規化
$angles = [-450, -90, 0, 90, 180, 450, 810];
foreach ($angles as $angle) {
    $normalized = normalize_angle($angle);
    echo "角度 {$angle}° は 0-360°の範囲内では {$normalized}°\n";
}

例5: 周期関数の実装

<?php
// 三角波関数の実装
function triangle_wave($x, $period = 1.0) {
    $normalized = fmod($x, $period) / $period;
    if ($normalized < 0) $normalized += 1.0;

    return $normalized < 0.5 ? 
           $normalized * 2 : 
           2 - $normalized * 2;
}

// 三角波関数の値をプロット
echo "三角波関数の値:\n";
for ($x = 0; $x <= 2; $x += 0.25) {
    $value = triangle_wave($x);
    $bar = str_repeat("*", (int)($value * 40));
    echo sprintf("x=%.2f: [%-40s] %.2f\n", $x, $bar, $value);
}

まとめ

fmod()関数は以下の場面で特に有用です:

  1. 浮動小数点数の余りを正確に計算する必要がある場合
  2. 循環パターンや周期性を持つプログラムの実装
  3. 金融計算などの高精度な数値演算
  4. グラフィックプログラミングでの角度や座標の計算

他の言語との互換性も高く、C言語の同名関数と同様の動作をするため、アルゴリズムを移植する際にも便利です。浮動小数点数を扱う際の精度の問題には注意が必要ですが、適切に使用することで多くの数学的処理を効率的に実装できます。

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