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)。
计算不同象限点的角度:
<?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 (原点)
?>
在坐标系转换中使用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";
}
?>
在物理和机器人学中应用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";
?>
在计算机图形学和游戏开发中使用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";
}
?>
高级应用中的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";
}
?>
对于任意实数 y 和 x(不同时为零),atan2(y, x) 定义为:
角度 θ ∈ (-π, π] 使得 x = r·cos(θ), y = r·sin(θ),其中 r = √(x² + y²)
等价于:θ = atan(y/x),但根据 (x, y) 的象限进行调整。
定义域: R² \ {(0,0)}
值域: (-π, π] 弧度
| 特性 | 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,但数学上原点的角度未定义atan2(NAN, x) 或 atan2(y, NAN) 返回 NANatan2(INF, INF) 返回 π/4(或相关值)atan()计算单个参数的反正切
sin()计算正弦值
cos()计算余弦值
hypot()计算直角三角形的斜边长度
deg2rad()将角度转换为弧度
rad2deg()将弧度转换为角度