PHP atan2()函数

四象限反正切: atan2() 函数返回两个变量(y, x)的反正切值,考虑两个参数的符号来确定正确的象限。

atan2() 函数是 atan() 函数的增强版本,它根据点的坐标 (x, y) 计算从 x 轴到该点的角度,返回值的范围是 -π 到 π 弧度。

函数语法

atan2(float $y, float $x): float

参数说明

参数 类型 描述 必需
$y float 点的y坐标(纵坐标)
$x float 点的x坐标(横坐标)

返回值

返回从 x 轴到点 (x, y) 的角度,以弧度为单位。返回值范围是 -π 到 π(约 -3.1415926535898 到 3.1415926535898)。

使用示例

示例1:基本用法 - 计算四象限角度

计算不同象限点的角度:

<?php
// 计算不同象限的角度
echo "atan2(0, 1) = " . atan2(0, 1) . " 弧度\n";         // 0° (正东)
echo "atan2(1, 0) = " . atan2(1, 0) . " 弧度\n";         // π/2 ≈ 1.5708 (正北)
echo "atan2(0, -1) = " . atan2(0, -1) . " 弧度\n";       // π ≈ 3.1416 (正西)
echo "atan2(-1, 0) = " . atan2(-1, 0) . " 弧度\n";       // -π/2 ≈ -1.5708 (正南)

// 第一象限
echo "atan2(1, 1) = " . atan2(1, 1) . " 弧度\n";         // π/4 ≈ 0.7854 (东北)
echo "atan2(1, 1) = " . rad2deg(atan2(1, 1)) . " 度\n";  // 45°

// 第二象限
echo "atan2(1, -1) = " . atan2(1, -1) . " 弧度\n";       // 3π/4 ≈ 2.3562 (西北)
echo "atan2(1, -1) = " . rad2deg(atan2(1, -1)) . " 度\n"; // 135°

// 第三象限
echo "atan2(-1, -1) = " . atan2(-1, -1) . " 弧度\n";     // -3π/4 ≈ -2.3562 (西南)
echo "atan2(-1, -1) = " . rad2deg(atan2(-1, -1)) . " 度\n"; // -135°

// 第四象限
echo "atan2(-1, 1) = " . atan2(-1, 1) . " 弧度\n";       // -π/4 ≈ -0.7854 (东南)
echo "atan2(-1, 1) = " . rad2deg(atan2(-1, 1)) . " 度\n";  // -45°

// 处理原点
echo "atan2(0, 0) = " . atan2(0, 0) . " 弧度\n";         // 0 (原点)
?>

示例2:坐标转换和角度计算

在坐标系转换中使用atan2():

<?php
// 创建一个坐标计算类
class CoordinateCalculator {

    // 计算点到原点的距离
    public static function distanceFromOrigin($x, $y) {
        return sqrt($x * $x + $y * $y);
    }

    // 计算点的极坐标角度(0到360度)
    public static function polarAngle($x, $y) {
        $angle_rad = atan2($y, $x);
        $angle_deg = rad2deg($angle_rad);

        // 转换为0到360度
        if ($angle_deg < 0) {
            $angle_deg += 360;
        }

        return [
            'radians' => $angle_rad,
            'degrees' => $angle_deg,
            'distance' => self::distanceFromOrigin($x, $y)
        ];
    }

    // 从极坐标转换为直角坐标
    public static function polarToCartesian($r, $theta_deg) {
        $theta_rad = deg2rad($theta_deg);
        return [
            'x' => $r * cos($theta_rad),
            'y' => $r * sin($theta_rad)
        ];
    }

    // 计算两个点之间的夹角
    public static function angleBetweenPoints($x1, $y1, $x2, $y2) {
        $angle1 = atan2($y1, $x1);
        $angle2 = atan2($y2, $x2);
        $delta = $angle2 - $angle1;

        // 规范化到[-π, π]范围
        if ($delta > M_PI) {
            $delta -= 2 * M_PI;
        } elseif ($delta < -M_PI) {
            $delta += 2 * M_PI;
        }

        return [
            'radians' => $delta,
            'degrees' => rad2deg($delta)
        ];
    }
}

// 使用示例
echo "极坐标转换:\n";
$points = [
    [1, 0],    // 正东
    [0, 1],    // 正北
    [-1, 0],   // 正西
    [0, -1],   // 正南
    [3, 4],    // 第一象限
    [-3, 4],   // 第二象限
    [-3, -4],  // 第三象限
    [3, -4]    // 第四象限
];

foreach ($points as $point) {
    $x = $point[0];
    $y = $point[1];
    $polar = CoordinateCalculator::polarAngle($x, $y);
    echo "点($x, $y): 距离 = " . round($polar['distance'], 2) .
         ", 角度 = " . round($polar['degrees'], 2) . "° (" . round($polar['radians'], 4) . " 弧度)\n";
}

echo "\n直角坐标转极坐标:\n";
$r = 10;
$angles = [0, 45, 90, 135, 180, -45, -90];
foreach ($angles as $angle) {
    $cartesian = CoordinateCalculator::polarToCartesian($r, $angle);
    echo "r=$r, θ=$angle° → x=" . round($cartesian['x'], 2) . ", y=" . round($cartesian['y'], 2) . "\n";
}

echo "\n计算两点间的夹角:\n";
$point_pairs = [
    [[1, 0], [0, 1]],      // 从东到北
    [[1, 0], [-1, 0]],     // 从东到西
    [[1, 1], [2, 2]],      // 同方向
    [[1, 0], [0, -1]]      // 从东到南
];

foreach ($point_pairs as $pair) {
    $x1 = $pair[0][0]; $y1 = $pair[0][1];
    $x2 = $pair[1][0]; $y2 = $pair[1][1];
    $angle = CoordinateCalculator::angleBetweenPoints($x1, $y1, $x2, $y2);
    echo "点1($x1,$y1) 到 点2($x2,$y2): 夹角 = " . round($angle['degrees'], 2) . "°\n";
}
?>

示例3:物理和机器人应用

在物理和机器人学中应用atan2()函数:

<?php
// 机器人运动控制类
class RobotController {

    // 计算机器人朝向目标的角度
    public static function headingToTarget($robot_x, $robot_y, $target_x, $target_y, $robot_heading_deg) {
        // 计算目标相对于机器人的位置
        $dx = $target_x - $robot_x;
        $dy = $target_y - $robot_y;

        // 计算目标相对于机器人的角度
        $target_angle_rad = atan2($dy, $dx);
        $target_angle_deg = rad2deg($target_angle_rad);

        // 计算需要转动的角度
        $turn_angle = $target_angle_deg - $robot_heading_deg;

        // 规范化到[-180, 180]度
        while ($turn_angle > 180) {
            $turn_angle -= 360;
        }
        while ($turn_angle < -180) {
            $turn_angle += 360;
        }

        return [
            'dx' => $dx,
            'dy' => $dy,
            'target_angle' => $target_angle_deg,
            'robot_heading' => $robot_heading_deg,
            'turn_angle' => $turn_angle
        ];
    }

    // 计算机械臂关节角度
    public static function calculateArmAngles($x, $y, $link1_length, $link2_length) {
        // 二维机械臂逆向运动学
        // 计算两个关节的角度

        // 计算到目标点的距离
        $distance = sqrt($x * $x + $y * $y);

        // 检查是否可达
        if ($distance > ($link1_length + $link2_length) || $distance < abs($link1_length - $link2_length)) {
            throw new InvalidArgumentException("目标点不可达");
        }

        // 计算关节2的角度(使用余弦定理)
        $cos_theta2 = ($x*$x + $y*$y - $link1_length*$link1_length - $link2_length*$link2_length) /
                      (2 * $link1_length * $link2_length);
        $theta2 = acos($cos_theta2);

        // 计算关节1的角度
        $beta = atan2($y, $x);
        $psi = atan2($link2_length * sin($theta2), $link1_length + $link2_length * cos($theta2));
        $theta1 = $beta - $psi;

        return [
            'theta1_rad' => $theta1,
            'theta2_rad' => $theta2,
            'theta1_deg' => rad2deg($theta1),
            'theta2_deg' => rad2deg($theta2)
        ];
    }
}

// 机器人导航示例
echo "机器人导航:\n";
$robot_x = 0;
$robot_y = 0;
$robot_heading = 90; // 机器人当前朝向90°(正北)
$target_x = 5;
$target_y = 5;

$result = RobotController::headingToTarget($robot_x, $robot_y, $target_x, $target_y, $robot_heading);
echo "机器人位置: ($robot_x, $robot_y), 朝向: {$robot_heading}°\n";
echo "目标位置: ($target_x, $target_y)\n";
echo "相对位移: dx=" . $result['dx'] . ", dy=" . $result['dy'] . "\n";
echo "目标角度: " . round($result['target_angle'], 2) . "°\n";
echo "需要转动的角度: " . round($result['turn_angle'], 2) . "°\n";

echo "\n机械臂逆向运动学:\n";
try {
    $link1 = 5;  // 第一个连杆长度
    $link2 = 3;  // 第二个连杆长度
    $target_x = 7;
    $target_y = 2;

    $angles = RobotController::calculateArmAngles($target_x, $target_y, $link1, $link2);

    echo "目标点: ($target_x, $target_y)\n";
    echo "连杆长度: L1=$link1, L2=$link2\n";
    echo "关节1角度: " . round($angles['theta1_deg'], 2) . "°\n";
    echo "关节2角度: " . round($angles['theta2_deg'], 2) . "°\n";

    // 验证正向运动学
    $x1 = $link1 * cos($angles['theta1_rad']);
    $y1 = $link1 * sin($angles['theta1_rad']);

    $x2 = $x1 + $link2 * cos($angles['theta1_rad'] + $angles['theta2_rad']);
    $y2 = $y1 + $link2 * sin($angles['theta1_rad'] + $angles['theta2_rad']);

    echo "验证正向运动学:\n";
    echo "计算端点: (" . round($x2, 4) . ", " . round($y2, 4) . ")\n";
    echo "目标端点: ($target_x, $target_y)\n";
    echo "误差: " . sqrt(pow($x2 - $target_x, 2) + pow($y2 - $target_y, 2)) . "\n";

} catch (Exception $e) {
    echo "错误: " . $e->getMessage() . "\n";
}

// 计算风速和风向
echo "\n风速和风向计算:\n";
function calculateWindVector($u, $v) {
    // u: 东西方向风速(东为正)
    // v: 南北方向风速(北为正)

    $speed = sqrt($u * $u + $v * $v);
    $direction_rad = atan2($v, $u);
    $direction_deg = rad2deg($direction_rad);

    // 风向通常表示为从哪个方向吹来,所以需要调整
    $wind_direction = 270 - $direction_deg;
    if ($wind_direction < 0) {
        $wind_direction += 360;
    }

    return [
        'speed' => $speed,
        'direction_rad' => $direction_rad,
        'direction_deg' => $direction_deg,
        'wind_direction' => $wind_direction
    ];
}

$u = 3;  // 东向风速 3 m/s
$v = 4;  // 北向风速 4 m/s
$wind = calculateWindVector($u, $v);

echo "东西分量 (u): $u m/s\n";
echo "南北分量 (v): $v m/s\n";
echo "风速: " . round($wind['speed'], 2) . " m/s\n";
echo "风向角度: " . round($wind['direction_deg'], 2) . "° (从正东逆时针)\n";
echo "气象风向: " . round($wind['wind_direction'], 2) . "° (从正北顺时针)\n";
?>

示例4:计算机图形学应用

在计算机图形学和游戏开发中使用atan2():

<?php
// 计算机图形学中的向量和角度计算
class GraphicsMath {

    // 计算两个向量之间的夹角
    public static function angleBetweenVectors($x1, $y1, $x2, $y2) {
        // 点积: A·B = |A||B|cosθ
        $dot_product = $x1 * $x2 + $y1 * $y2;
        $length1 = sqrt($x1 * $x1 + $y1 * $y1);
        $length2 = sqrt($x2 * $x2 + $y2 * $y2);

        // 避免除零
        if ($length1 == 0 || $length2 == 0) {
            return 0;
        }

        $cos_theta = $dot_product / ($length1 * $length2);
        $cos_theta = max(-1, min(1, $cos_theta)); // 避免浮点误差

        $angle = acos($cos_theta);

        // 使用atan2确定角度的符号(叉积的z分量)
        $cross_z = $x1 * $y2 - $y1 * $x2;
        if ($cross_z < 0) {
            $angle = -$angle;
        }

        return [
            'radians' => $angle,
            'degrees' => rad2deg($angle),
            'dot_product' => $dot_product,
            'cross_z' => $cross_z
        ];
    }

    // 计算点的旋转
    public static function rotatePoint($x, $y, $angle_deg, $center_x = 0, $center_y = 0) {
        $angle_rad = deg2rad($angle_deg);
        $cos_theta = cos($angle_rad);
        $sin_theta = sin($angle_rad);

        // 平移以中心点为原点
        $translated_x = $x - $center_x;
        $translated_y = $y - $center_y;

        // 旋转
        $rotated_x = $translated_x * $cos_theta - $translated_y * $sin_theta;
        $rotated_y = $translated_x * $sin_theta + $translated_y * $cos_theta;

        // 平移回原位置
        return [
            'x' => $rotated_x + $center_x,
            'y' => $rotated_y + $center_y
        ];
    }

    // 计算鼠标点击相对于中心点的角度(用于游戏开发)
    public static function mouseAngleFromCenter($mouse_x, $mouse_y, $center_x, $center_y) {
        $dx = $mouse_x - $center_x;
        $dy = $mouse_y - $center_y;

        $angle_rad = atan2($dy, $dx);
        $angle_deg = rad2deg($angle_rad);

        // 转换为0-360度
        if ($angle_deg < 0) {
            $angle_deg += 360;
        }

        return [
            'dx' => $dx,
            'dy' => $dy,
            'angle_rad' => $angle_rad,
            'angle_deg' => $angle_deg
        ];
    }
}

// 使用示例
echo "向量角度计算:\n";
$vectors = [
    [[1, 0], [0, 1]],      // 垂直
    [[1, 0], [1, 0]],      // 相同方向
    [[1, 0], [-1, 0]],     // 相反方向
    [[1, 0], [1, 1]],      // 45度
    [[1, 2], [3, 4]]       // 任意向量
];

foreach ($vectors as $i => $vector_pair) {
    $x1 = $vector_pair[0][0]; $y1 = $vector_pair[0][1];
    $x2 = $vector_pair[1][0]; $y2 = $vector_pair[1][1];

    $result = GraphicsMath::angleBetweenVectors($x1, $y1, $x2, $y2);
    echo "向量1($x1,$y1) 和 向量2($x2,$y2):\n";
    echo "  角度: " . round($result['degrees'], 2) . "°\n";
    echo "  点积: " . round($result['dot_product'], 2) . "\n";
    echo "  叉积(z): " . round($result['cross_z'], 2) . " (方向)\n";
}

echo "\n点旋转:\n";
$point = [10, 0];
$center = [0, 0];
$angles = [45, 90, 180, 270];

foreach ($angles as $angle) {
    $rotated = GraphicsMath::rotatePoint($point[0], $point[1], $angle, $center[0], $center[1]);
    echo "点({$point[0]},{$point[1]}) 绕({$center[0]},{$center[1]}) 旋转{$angle}° → (" .
         round($rotated['x'], 2) . ", " . round($rotated['y'], 2) . ")\n";
}

echo "\n鼠标角度计算(游戏开发):\n";
$center_x = 400;
$center_y = 300;
$mouse_positions = [
    [500, 300],  // 右边
    [400, 200],  // 上边
    [300, 300],  // 左边
    [400, 400],  // 下边
    [450, 250]   // 右上
];

foreach ($mouse_positions as $mouse) {
    $mouse_x = $mouse[0];
    $mouse_y = $mouse[1];
    $angle_info = GraphicsMath::mouseAngleFromCenter($mouse_x, $mouse_y, $center_x, $center_y);
    echo "鼠标位置($mouse_x,$mouse_y) 相对于中心($center_x,$center_y):\n";
    echo "  角度: " . round($angle_info['angle_deg'], 2) . "°\n";
    echo "  方向: ";

    if ($angle_info['angle_deg'] >= 315 || $angle_info['angle_deg'] < 45) {
        echo "右";
    } elseif ($angle_info['angle_deg'] >= 45 && $angle_info['angle_deg'] < 135) {
        echo "上";
    } elseif ($angle_info['angle_deg'] >= 135 && $angle_info['angle_deg'] < 225) {
        echo "左";
    } else {
        echo "下";
    }
    echo "\n";
}

// 计算太阳位置(游戏中的日/夜循环)
echo "\n太阳位置计算(游戏开发):\n";
class SunPosition {
    public static function calculate($time_of_day, $latitude = 35, $longitude = 120) {
        // 简化版的太阳位置计算
        // time_of_day: 0-24小时

        // 将时间转换为太阳时角(简化)
        $hour_angle = ($time_of_day - 12) * 15; // 每小时15度

        // 计算太阳高度角(简化)
        $declination = 23.45 * sin(deg2rad((284 + $time_of_day * 15) % 365)); // 太阳赤纬

        $sin_altitude = sin(deg2rad($latitude)) * sin(deg2rad($declination)) +
                       cos(deg2rad($latitude)) * cos(deg2rad($declination)) * cos(deg2rad($hour_angle));

        $altitude = rad2deg(asin($sin_altitude));

        // 计算太阳方位角(使用atan2)
        $sin_azimuth = -cos(deg2rad($declination)) * sin(deg2rad($hour_angle)) / cos(deg2rad($altitude));
        $cos_azimuth = (sin(deg2rad($declination)) * cos(deg2rad($latitude)) -
                       cos(deg2rad($declination)) * sin(deg2rad($latitude)) * cos(deg2rad($hour_angle))) /
                       cos(deg2rad($altitude));

        $azimuth = rad2deg(atan2($sin_azimuth, $cos_azimuth));

        if ($azimuth < 0) {
            $azimuth += 360;
        }

        return [
            'time' => $time_of_day,
            'altitude' => $altitude,
            'azimuth' => $azimuth,
            'is_day' => $altitude > 0
        ];
    }
}

$times = [6, 9, 12, 15, 18];
foreach ($times as $time) {
    $sun = SunPosition::calculate($time);
    echo "时间: " . sprintf("%02d:00", $time) . ":\n";
    echo "  太阳高度: " . round($sun['altitude'], 2) . "°\n";
    echo "  太阳方位: " . round($sun['azimuth'], 2) . "° (从北顺时针)\n";
    echo "  是否白天: " . ($sun['is_day'] ? "是" : "否") . "\n";
}
?>

示例5:高级应用和错误处理

高级应用中的atan2()函数和错误处理:

<?php
// 安全的atan2计算器
class SafeAtan2Calculator {

    // 安全的atan2计算,处理特殊情况
    public static function safeAtan2($y, $x) {
        // 检查输入类型
        if (!is_numeric($y) || !is_numeric($x)) {
            throw new InvalidArgumentException("参数必须是数字");
        }

        // 处理特殊值
        if (is_nan($y) || is_nan($x)) {
            return NAN;
        }

        // 处理无穷大
        if (is_infinite($y) || is_infinite($x)) {
            return self::atan2Infinity($y, $x);
        }

        // 处理原点
        if (abs($y) < 1e-15 && abs($x) < 1e-15) {
            return 0.0; // 原点的atan2通常定义为0
        }

        // 计算atan2
        return atan2($y, $x);
    }

    // 处理无穷大的特殊情况
    private static function atan2Infinity($y, $x) {
        if (is_infinite($y) && is_infinite($x)) {
            if ($y > 0) {
                return $x > 0 ? M_PI_4 : (3 * M_PI_4);  // π/4 或 3π/4
            } else {
                return $x > 0 ? -M_PI_4 : (-3 * M_PI_4); // -π/4 或 -3π/4
            }
        }

        if (is_infinite($y)) {
            return $y > 0 ? M_PI_2 : -M_PI_2;
        }

        if (is_infinite($x)) {
            return $x > 0 ? 0.0 : M_PI;
        }

        return atan2($y, $x);
    }

    // 批量计算atan2
    public static function batchAtan2(array $pairs) {
        $results = [];
        foreach ($pairs as $index => $pair) {
            try {
                if (count($pair) != 2) {
                    throw new InvalidArgumentException("每个元素必须是[y, x]对");
                }

                $y = $pair[0];
                $x = $pair[1];

                $result = self::safeAtan2($y, $x);
                $results[$index] = [
                    'input' => [$y, $x],
                    'result' => $result,
                    'result_deg' => rad2deg($result),
                    'status' => 'success'
                ];
            } catch (Exception $e) {
                $results[$index] = [
                    'input' => $pair,
                    'result' => null,
                    'status' => 'error',
                    'message' => $e->getMessage()
                ];
            }
        }
        return $results;
    }

    // 计算角度的平均值(考虑角度环绕)
    public static function averageAngles(array $angles_rad) {
        if (empty($angles_rad)) {
            throw new InvalidArgumentException("角度数组不能为空");
        }

        // 使用向量和的方法计算平均角度
        $sum_sin = 0;
        $sum_cos = 0;

        foreach ($angles_rad as $angle) {
            $sum_sin += sin($angle);
            $sum_cos += cos($angle);
        }

        // 计算平均角度
        $avg_angle = atan2($sum_sin, $sum_cos);

        return [
            'sum_sin' => $sum_sin,
            'sum_cos' => $sum_cos,
            'avg_rad' => $avg_angle,
            'avg_deg' => rad2deg($avg_angle)
        ];
    }
}

// 测试边界条件和特殊值
echo "测试边界条件和特殊值:\n";
$testCases = [
    [0, 0],            // 原点
    [0, 1],            // 正x轴
    [1, 0],            // 正y轴
    [0, -1],           // 负x轴
    [-1, 0],           // 负y轴
    [INF, 1],          // y无穷大
    [1, INF],          // x无穷大
    [INF, INF],        // 都无穷大
    [-INF, INF],       // y负无穷,x正无穷
    [NAN, 1],          // y是NaN
    [1, NAN],          // x是NaN
    ["abc", 1],        // 非数字
    [1e-20, 1e-20],    // 极小值
    [1e20, 1e20],      // 极大值
    [M_PI, M_E]        // 特殊常数
];

foreach ($testCases as $i => $case) {
    $y = $case[0];
    $x = $case[1];

    echo "测试 " . ($i+1) . ": atan2(";
    echo is_nan($y) ? "NAN" : (is_infinite($y) ? ($y > 0 ? "INF" : "-INF") : $y);
    echo ", ";
    echo is_nan($x) ? "NAN" : (is_infinite($x) ? ($x > 0 ? "INF" : "-INF") : $x);
    echo ") = ";

    try {
        $result = SafeAtan2Calculator::safeAtan2($y, $x);

        if (is_nan($result)) {
            echo "NAN";
        } elseif (is_infinite($result)) {
            echo $result > 0 ? "INF" : "-INF";
        } else {
            echo round($result, 6) . " 弧度 (" . round(rad2deg($result), 2) . "°)";
        }
    } catch (Exception $e) {
        echo "错误: " . $e->getMessage();
    }

    echo "\n";
}

echo "\n批量计算atan2:\n";
$pairs = [
    [1, 1],
    [1, -1],
    [-1, 1],
    [-1, -1],
    [0, 0],
    [3, 4]
];

$batchResults = SafeAtan2Calculator::batchAtan2($pairs);
foreach ($batchResults as $index => $result) {
    echo "对" . ($index+1) . ": ";
    if ($result['status'] === 'success') {
        $input = $result['input'];
        echo "atan2(" . $input[0] . ", " . $input[1] . ") = " .
             round($result['result'], 4) . " 弧度 (" . round($result['result_deg'], 2) . "°)";
    } else {
        echo "错误: " . $result['message'];
    }
    echo "\n";
}

echo "\n计算角度的平均值(考虑角度环绕):\n";
$angles_deg = [350, 10, 20];  // 这些角度的平均值应该是10°
$angles_rad = array_map('deg2rad', $angles_deg);

$avg = SafeAtan2Calculator::averageAngles($angles_rad);
echo "角度: " . implode('°, ', $angles_deg) . "°\n";
echo "正弦和: " . round($avg['sum_sin'], 4) . "\n";
echo "余弦和: " . round($avg['sum_cos'], 4) . "\n";
echo "平均角度: " . round($avg['avg_deg'], 2) . "° (" . round($avg['avg_rad'], 4) . " 弧度)\n";

echo "\n验证角度平均:\n";
$test_angles_sets = [
    [0, 120, 240],  // 这三个角度平均应该是0°(实际上是120°和240°)
    [45, 135, 225, 315],
    [10, 20, 30],
    [179, 181]  // 这两个角度的平均应该是180°
];

foreach ($test_angles_sets as $angles) {
    $angles_rad = array_map('deg2rad', $angles);
    $avg = SafeAtan2Calculator::averageAngles($angles_rad);
    echo "角度 [" . implode('°, ', $angles) . "°] 的平均值: " . round($avg['avg_deg'], 2) . "°\n";
}

// 计算atan2的导数
echo "\natan2函数的导数:\n";
function atan2PartialDerivativeY($x, $y) {
    // ∂/∂y atan2(y,x) = x/(x²+y²)
    return $x / ($x * $x + $y * $y);
}

function atan2PartialDerivativeX($x, $y) {
    // ∂/∂x atan2(y,x) = -y/(x²+y²)
    return -$y / ($x * $x + $y * $y);
}

function numericalPartialDerivative($func, $var, $x, $y, $h = 1e-7) {
    // 数值计算偏导数
    if ($var === 'y') {
        return (atan2($y + $h, $x) - atan2($y - $h, $x)) / (2 * $h);
    } else {
        return (atan2($y, $x + $h) - atan2($y, $x - $h)) / (2 * $h);
    }
}

$points = [
    [1, 0],
    [0, 1],
    [1, 1],
    [2, 3]
];

foreach ($points as $point) {
    $x = $point[0];
    $y = $point[1];

    $exact_dy = atan2PartialDerivativeY($x, $y);
    $exact_dx = atan2PartialDerivativeX($x, $y);

    $numeric_dy = numericalPartialDerivative('atan2', 'y', $x, $y);
    $numeric_dx = numericalPartialDerivative('atan2', 'x', $x, $y);

    echo "点($x, $y):\n";
    echo "  ∂atan2/∂y: 精确 = " . round($exact_dy, 6) . ", 数值 = " . round($numeric_dy, 6) .
         ", 误差 = " . sprintf("%.2e", abs($exact_dy - $numeric_dy)) . "\n";
    echo "  ∂atan2/∂x: 精确 = " . round($exact_dx, 6) . ", 数值 = " . round($numeric_dx, 6) .
         ", 误差 = " . sprintf("%.2e", abs($exact_dx - $numeric_dx)) . "\n";
}
?>

数学原理

atan2()函数定义

对于任意实数 y 和 x(不同时为零),atan2(y, x) 定义为:

角度 θ ∈ (-π, π] 使得 x = r·cos(θ), y = r·sin(θ),其中 r = √(x² + y²)

等价于:θ = atan(y/x),但根据 (x, y) 的象限进行调整。

定义域: R² \ {(0,0)}

值域: (-π, π] 弧度

重要性质和公式
  • 象限判断:自动根据 (x, y) 符号确定正确象限
  • 原点处理:atan2(0, 0) 通常定义为 0
  • 奇偶性:atan2(-y, x) = -atan2(y, x)
  • 对称性:atan2(y, -x) = π - atan2(y, x)
  • 偏导数:∂/∂y atan2(y,x) = x/(x²+y²)
  • 偏导数:∂/∂x atan2(y,x) = -y/(x²+y²)

与atan()的区别

atan() vs atan2() 详细对比
特性 atan($y/$x) atan2($y, $x)
参数 1个参数(商) 2个参数(分子,分母)
值域 (-π/2, π/2) (-π, π]
象限判断 无法判断 自动判断
除零处理 需要手动处理 自动处理
原点 需要特殊处理 atan2(0,0)=0
推荐场景 已知比值的简单计算 坐标转换、角度计算

象限判断规则

象限 x值 y值 atan2(y,x) 范围 atan(y/x) 范围
第一象限 x > 0 y > 0 (0, π/2) (0, π/2)
第二象限 x < 0 y > 0 (π/2, π) (-π/2, 0) + π
第三象限 x < 0 y < 0 (-π, -π/2) (0, π/2) - π
第四象限 x > 0 y < 0 (-π/2, 0) (-π/2, 0)
坐标轴 x = 0 或 y = 0 特殊值 0, ±π/2, ±π 需要特殊处理

应用场景

场景 描述 公式/代码
极坐标转换 直角坐标转极坐标角度 atan2($y, $x)
机器人导航 计算朝向目标的角度 atan2($dy, $dx)
游戏开发 计算鼠标/触摸屏角度 atan2($mouse_y-$center_y, $mouse_x-$center_x)
气象学 计算风向角度 atan2($v, $u)
计算机图形学 计算向量方向角 atan2($vector_y, $vector_x)

注意事项

重要提醒
  • 参数顺序:atan2($y, $x) 第一个参数是y坐标,第二个是x坐标
  • 原点处理:atan2(0, 0) 返回 0,但数学上原点的角度未定义
  • 精度问题:当x和y非常大或非常小时,可能存在精度损失
  • 特殊值:atan2(NAN, x)atan2(y, NAN) 返回 NAN
  • 无穷大:atan2(INF, INF) 返回 π/4(或相关值)

相关函数

atan()

计算单个参数的反正切

sin()

计算正弦值

cos()

计算余弦值

hypot()

计算直角三角形的斜边长度

deg2rad()

将角度转换为弧度

rad2deg()

将弧度转换为角度

常见问题

这是历史原因和数学惯例。atan2(y, x) 对应着计算 atan(y/x),即纵坐标除以横坐标。这种顺序在许多编程语言和数学库中都是一致的,包括C、C++、Java、Python等。记住它的一个简单方法是:atan2(对边, 邻边),就像在直角三角形中计算角度一样。

这个范围覆盖了完整的360度圆周,其中-π到0表示下半平面(第三、四象限),0到π表示上半平面(第一、二象限)。选择(-π, π]而不是[0, 2π)是因为这样保持了atan2(y, x)是y的奇函数:atan2(-y, x) = -atan2(y, x)。这种选择在数学上更自然,特别是在处理对称性和奇偶性时。

  1. 原点(0,0):atan2(0,0)通常返回0,但数学上未定义。根据应用需要,可能需要特殊处理
  2. 坐标轴:atan2(正数,0)=π/2,atan2(负数,0)=-π/2,atan2(0,正数)=0,atan2(0,负数)=π
  3. 无穷大:atan2(±∞, x)返回±π/2,atan2(y, ±∞)返回0或π,atan2(±∞, ±∞)返回±π/4或±3π/4
  4. NaN:任何参数为NaN时返回NaN
  5. 精度问题:对于极大或极小的值,使用安全计算函数或检查输入范围