1. 为什么我们需要关注数学函数与头文件
在C++项目开发中,数学运算无处不在。从简单的游戏物理引擎到复杂的金融算法,高效精确的数学计算能力往往决定着程序的性能和可靠性。传统上,我们使用
C++20引入的
2. 传统函数的深度解析与优化技巧
2.1 常用数学函数性能对比
让我们通过一个简单的基准测试来比较不同数学函数的性能:
cpp复制#include <benchmark/benchmark.h>
#include <cmath>
static void BM_Sqrt(benchmark::State& state) {
for (auto _ : state) {
benchmark::DoNotOptimize(std::sqrt(2.0));
}
}
BENCHMARK(BM_Sqrt);
static void BM_Exp(benchmark::State& state) {
for (auto _ : state) {
benchmark::DoNotOptimize(std::exp(1.0));
}
}
BENCHMARK(BM_Exp);
在我的i9-13900K测试平台上,结果显示出明显的性能差异:
| 函数 | 平均耗时(ns) | 吞吐量(M次/秒) |
|---|---|---|
| std::sqrt | 3.2 | 312.5 |
| std::exp | 18.7 | 53.5 |
关键发现:不同数学函数的性能差异可达5倍以上,在热点路径上应谨慎选择
2.2 精度问题的实战解决方案
考虑计算复利这个常见场景:
cpp复制double calculateInterest(double principal, double rate, int years) {
return principal * std::pow(1 + rate, years); // 潜在精度问题!
}
这里存在两个隐患:
- 小利率情况下,1+rate可能丢失精度
- pow函数对于大指数可能产生溢出
改进方案:
cpp复制#include <boost/math/special_functions.hpp>
double safeCalculateInterest(double principal, double rate, int years) {
return principal * boost::math::pow(1 + rate, years);
}
3. C++20 头文件的完全指南
3.1 数学常量的类型安全访问
cpp复制#include <numbers>
constexpr float pi_float = std::numbers::pi_v<float>;
constexpr double pi_double = std::numbers::pi_v<double>;
对比传统方式:
cpp复制#define _USE_MATH_DEFINES
#include <cmath>
const double pi = M_PI; // 类型不安全,可能在不同平台有不同定义
3.2 编译时数学计算
C++20的
cpp复制consteval double sphereVolume(double r) {
return (4.0 / 3.0) * std::numbers::pi_v<double> * r * r * r;
}
static_assert(sphereVolume(1.0) > 4.18879 && sphereVolume(1.0) < 4.1888);
4. 高级应用:构建类型安全的数学库
4.1 单位系统的实现
结合
cpp复制template<typename T, typename U>
class Quantity {
T value;
public:
explicit constexpr Quantity(T v) : value(v) {}
template<typename Other>
requires std::convertible_to<Other, T>
explicit constexpr Quantity(Quantity<Other, U> q) : value(q.getValue()) {}
constexpr T getValue() const { return value; }
// 运算符重载...
};
using Meter = Quantity<double, struct MeterTag>;
using Second = Quantity<double, struct SecondTag>;
using Newton = Quantity<double, struct NewtonTag>;
constexpr Meter operator""_m(long double val) {
return Meter{static_cast<double>(val)};
}
4.2 SIMD加速数学运算
现代CPU的SIMD指令可以大幅提升数学函数性能。以AVX2为例:
cpp复制#include <immintrin.h>
void vectorizedExp(float* input, float* output, size_t n) {
const __m256 one = _mm256_set1_ps(1.0f);
for (size_t i = 0; i < n; i += 8) {
__m256 x = _mm256_loadu_ps(input + i);
__m256 result = _mm256_exp_ps(x); // 使用近似算法实现
_mm256_storeu_ps(output + i, result);
}
}
5. 性能优化与陷阱规避
5.1 常见性能陷阱
-
隐式函数重载:
cpp复制float x = 1.0f; auto y = std::sin(x); // 可能调用double版本的sin正确做法:
cpp复制float y = std::sinf(x); // 明确使用float版本 -
异常处理缺失:
cpp复制try { double x = std::sqrt(-1.0); // 可能不抛出异常 } catch(...) { /* 不会被捕获 */ }解决方案:
cpp复制#include <cfenv> std::feclearexcept(FE_ALL_EXCEPT); double x = std::sqrt(-1.0); if (std::fetestexcept(FE_INVALID)) { // 处理无效操作 }
5.2 编译器优化技巧
-
使用
-ffast-math标志时要小心:bash复制g++ -O3 -ffast-math main.cpp # 可能改变数学行为 -
特定函数的优化:
cpp复制__attribute__((optimize("fast-math"))) double hotspotFunction(double x) { return std::sin(x) * std::cos(x); }
6. 跨平台一致性的实现方案
6.1 浮点一致性保障
不同平台下浮点运算结果可能不同,解决方案:
cpp复制#include <cfenv>
#pragma STDC FENV_ACCESS ON
void consistentMath() {
std::fesetround(FE_TOWARDZERO); // 设置统一的舍入模式
// 关键数学运算...
}
6.2 自定义数学函数实现
对于需要完全一致性的场景,可以考虑实现自己的数学函数:
cpp复制namespace my_math {
constexpr double pi = 3.14159265358979323846;
double sin(double x) {
// 使用泰勒展开实现
double term = x;
double sum = term;
for (int n = 1; n < 10; ++n) {
term *= -x * x / ((2*n) * (2*n+1));
sum += term;
}
return sum;
}
}
7. 数学函数在现代C++项目中的最佳实践
7.1 模板元编程与数学计算
结合C++20的
cpp复制template<typename T>
constexpr T circleArea(T radius) {
return std::numbers::pi_v<T> * radius * radius;
}
static_assert(circleArea(1.0f) == std::numbers::pi_v<float>);
7.2 数学函数的constexpr化
C++20允许更多数学函数在编译期计算:
cpp复制constexpr double sqrt2 = std::sqrt(2.0); // C++20起支持
7.3 数学函数的选择策略
根据场景选择最佳实现:
| 使用场景 | 推荐实现 | 理由 |
|---|---|---|
| 高性能计算 | 编译器内置函数 | 最优化的机器指令 |
| 跨平台一致性 | 确保结果一致 | |
| 编译期计算 | constexpr函数 | 零运行时开销 |
| 高精度需求 | 多精度库(如GMP) | 避免浮点误差累积 |
8. 实战:构建一个科学计算模块
让我们实现一个完整的科学计算模块示例:
cpp复制#include <numbers>
#include <concepts>
#include <type_traits>
namespace sci_comp {
template<std::floating_point T>
constexpr T degreesToRadians(T degrees) {
return degrees * std::numbers::pi_v<T> / 180;
}
template<std::floating_point T>
struct Vector3 {
T x, y, z;
constexpr T length() const {
return std::sqrt(x*x + y*y + z*z);
}
constexpr Vector3 normalized() const {
T len = length();
return {x/len, y/len, z/len};
}
};
template<std::floating_point T>
constexpr T dot(const Vector3<T>& a, const Vector3<T>& b) {
return a.x*b.x + a.y*b.y + a.z*b.z;
}
}
使用示例:
cpp复制constexpr auto v = sci_comp::Vector3<float>{1,2,3}.normalized();
static_assert(std::abs(v.length() - 1.0f) < 1e-6f);
9. 性能敏感场景的优化技巧
9.1 查表法优化三角函数
对于需要频繁调用三角函数的场景:
cpp复制class SinTable {
static constexpr size_t TABLE_SIZE = 1024;
std::array<float, TABLE_SIZE> table;
public:
SinTable() {
for (size_t i = 0; i < TABLE_SIZE; ++i) {
table[i] = std::sin(2 * std::numbers::pi_v<float> * i / TABLE_SIZE);
}
}
float lookup(float x) const {
x = std::fmod(x, 2 * std::numbers::pi_v<float>);
size_t idx = static_cast<size_t>(x * TABLE_SIZE / (2 * std::numbers::pi_v<float>));
return table[idx % TABLE_SIZE];
}
};
9.2 多项式近似
对于不需要极高精度的场景:
cpp复制float fastExp(float x) {
x = 1.0f + x / 1024.0f;
x *= x; x *= x; x *= x; x *= x;
x *= x; x *= x; x *= x; x *= x;
x *= x; x *= x;
return x;
}
10. 调试与验证数学代码
10.1 单元测试策略
使用Catch2测试框架示例:
cpp复制#include <catch2/catch_test_macros.hpp>
#include <numbers>
TEST_CASE("Trigonometric functions") {
constexpr double tol = 1e-10;
REQUIRE(std::abs(std::sin(std::numbers::pi / 2) - 1.0) < tol);
REQUIRE(std::abs(std::cos(std::numbers::pi) + 1.0) < tol);
}
10.2 浮点异常检测
cpp复制#include <cfenv>
#include <iostream>
void enableMathExceptions() {
feenableexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW);
}
int main() {
enableMathExceptions();
try {
double x = std::sqrt(-1.0); // 将触发SIGFPE
} catch(...) {
std::cerr << "Math exception occurred\n";
}
}
11. 数学函数与并行计算的结合
11.1 使用OpenMP并行化数学计算
cpp复制#include <omp.h>
void parallelMath(std::vector<double>& input, std::vector<double>& output) {
#pragma omp parallel for
for (size_t i = 0; i < input.size(); ++i) {
output[i] = std::exp(input[i]) * std::numbers::sqrt2_v<double>;
}
}
11.2 GPU加速示例(CUDA)
cpp复制__global__ void gpuExp(double* input, double* output, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < n) {
output[idx] = exp(input[idx]);
}
}
12. 数学函数在现代C++中的未来发展
C++23和未来标准可能会引入:
- 更多编译期数学函数
- 标准化的SIMD数学运算
- 更完善的浮点异常处理
- 与
配套的数学算法库
临时解决方案示例:
cpp复制namespace future_std {
template<typename T>
constexpr T hypot(T x, T y, T z) {
return std::sqrt(x*x + y*y + z*z);
}
}
13. 实际工程中的经验总结
-
精度选择策略:
- 图形处理:float通常足够
- 科学计算:double是安全选择
- 金融计算:考虑decimal类型
-
性能与精度平衡:
cpp复制// 快速但低精度 float fast = std::sinf(angle); // 慢但高精度 double precise = std::sin(angle); -
平台差异处理:
cpp复制#if defined(_WIN32) constexpr double platformPi = 3.14159265358979323846; #else constexpr double platformPi = std::numbers::pi_v<double>; #endif
14. 构建数学函数性能测试框架
完整的测试框架示例:
cpp复制#include <chrono>
#include <iostream>
#include <cmath>
#include <numbers>
template<typename Func>
void benchmark(const char* name, Func&& f, int iterations = 1'000'000) {
auto start = std::chrono::high_resolution_clock::now();
volatile double result = 0; // 防止优化
for (int i = 0; i < iterations; ++i) {
result += f(i);
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << name << ": "
<< std::chrono::duration_cast<std::chrono::nanoseconds>(end-start).count()/iterations
<< " ns/op\n";
}
int main() {
benchmark("std::sin", [](int i) { return std::sin(i * 0.001); });
benchmark("std::numbers::sqrt2", [](int) { return std::numbers::sqrt2_v<double>; });
}
15. 数学函数错误处理的最佳实践
15.1 错误返回值与异常的结合
cpp复制class MathResult {
double value;
std::error_code ec;
public:
MathResult(double v, std::error_code e = {}) : value(v), ec(e) {}
explicit operator bool() const { return !ec; }
double get() const {
if (ec) throw std::system_error(ec);
return value;
}
};
MathResult safeSqrt(double x) {
if (x < 0) return {0, std::make_error_code(std::errc::invalid_argument)};
return {std::sqrt(x)};
}
15.2 数学运算的边界检查
cpp复制template<typename T>
constexpr T checkedMultiply(T a, T b) {
if constexpr (std::is_floating_point_v<T>) {
if (std::isinf(a * b)) throw std::overflow_error("Multiplication overflow");
}
return a * b;
}
16. 数学函数在模板元编程中的高级应用
16.1 编译期函数生成
cpp复制template<auto N>
constexpr auto makeSinTable() {
std::array<double, N> table{};
for (size_t i = 0; i < N; ++i) {
table[i] = std::sin(2 * std::numbers::pi_v<double> * i / N);
}
return table;
}
constexpr auto sinTable = makeSinTable<1024>();
16.2 数学函数的类型萃取
cpp复制template<typename F>
struct MathFunctionTraits;
template<>
struct MathFunctionTraits<decltype(std::sin)> {
using result_type = double;
static constexpr bool is_thread_safe = true;
static constexpr int required_stack_size = 1024;
};
template<typename F>
constexpr bool is_thread_safe_math_function =
MathFunctionTraits<F>::is_thread_safe;
17. 数学函数与STL算法的结合
17.1 使用数学函数作为算法参数
cpp复制#include <algorithm>
#include <vector>
void transformWithMath(std::vector<double>& data) {
std::transform(data.begin(), data.end(), data.begin(),
[](double x) {
return std::sin(x) * std::numbers::sqrt2_v<double>;
});
}
17.2 数学谓词的使用
cpp复制std::vector<double> filterSpecialValues(const std::vector<double>& input) {
std::vector<double> result;
std::copy_if(input.begin(), input.end(), std::back_inserter(result),
[](double x) {
return std::isfinite(x) &&
!std::isnan(x) &&
x != std::numbers::pi_v<double>;
});
return result;
}
18. 数学函数的自定义扩展
18.1 实现特殊数学函数
cpp复制double erfApprox(double x) {
// Abramowitz and Stegun近似公式
const double a1 = 0.254829592;
const double a2 = -0.284496736;
const double a3 = 1.421413741;
const double a4 = -1.453152027;
const double a5 = 1.061405429;
const double p = 0.3275911;
int sign = x < 0 ? -1 : 1;
x = std::abs(x);
double t = 1.0 / (1.0 + p * x);
double y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * std::exp(-x * x);
return sign * y;
}
18.2 数学函数的组合与管道
cpp复制template<typename... Fs>
auto compose(Fs... fs) {
return [=](auto x) {
return (fs(x), ...);
};
}
auto complexMathOp = compose(
[](double x) { return std::sin(x); },
[](double x) { return std::exp(x); },
[](double x) { return std::log(x); }
);
19. 数学函数在多线程环境中的使用
19.1 线程安全的数学函数封装
cpp复制class ThreadSafeMath {
mutable std::mutex mtx;
std::unordered_map<std::thread::id, std::unique_ptr<std::mt19937>> generators;
public:
double random() {
std::lock_guard lock(mtx);
auto tid = std::this_thread::get_id();
if (!generators[tid]) {
generators[tid] = std::make_unique<std::mt19937>(tid.hash());
}
std::uniform_real_distribution<double> dist(0, 1);
return dist(*generators[tid]);
}
};
19.2 数学运算的并行归约
cpp复制#include <execution>
double parallelSumOfSquares(const std::vector<double>& data) {
return std::transform_reduce(
std::execution::par,
data.begin(), data.end(),
0.0,
std::plus<>(),
[](double x) { return x * x; }
);
}
20. 数学函数在嵌入式系统中的特殊考量
20.1 低精度环境下的数学运算
cpp复制// 使用Q格式定点数
using Q15 = int16_t; // Q1.15格式
Q15 fixedSin(Q15 x) {
// 使用查表法实现定点数sin函数
static constexpr Q15 sinTable[256] = { /* ... */ };
return sinTable[(x >> 8) & 0xFF];
}
20.2 无浮点单元环境的处理
cpp复制// 软件实现的浮点运算
struct SoftFloat {
int32_t mantissa;
int16_t exponent;
};
SoftFloat softAdd(SoftFloat a, SoftFloat b) {
// 对齐指数后相加尾数
// ...
}
21. 数学函数的调试与性能分析技巧
21.1 使用GDB调试数学代码
常用GDB命令:
bash复制break std::pow
watch *(double*)0x7ffc12345678
set print floating-point on
21.2 性能分析工具的使用
使用perf分析数学函数热点:
bash复制perf record -g ./math_app
perf report -n --stdio
22. 数学函数在数值优化算法中的应用
22.1 梯度下降实现
cpp复制template<typename Func, typename Grad>
double gradientDescent(Func f, Grad grad, double x0, double lr, int steps) {
double x = x0;
for (int i = 0; i < steps; ++i) {
double g = grad(x);
if (std::abs(g) < 1e-10) break;
x -= lr * g;
}
return x;
}
22.2 牛顿法求根
cpp复制template<typename Func, typename Deriv>
double newtonMethod(Func f, Deriv df, double x0, double tol, int max_iter) {
double x = x0;
for (int i = 0; i < max_iter; ++i) {
double fx = f(x);
if (std::abs(fx) < tol) return x;
double dfx = df(x);
if (dfx == 0) break; // 避免除以零
x -= fx / dfx;
}
return x;
}
23. 数学函数与机器学习
23.1 激活函数实现
cpp复制template<typename T>
T sigmoid(T x) {
return 1 / (1 + std::exp(-x));
}
template<typename T>
T relu(T x) {
return x > 0 ? x : 0;
}
23.2 损失函数计算
cpp复制template<typename It>
double meanSquaredError(It pred_begin, It pred_end, It true_begin) {
double sum = 0;
size_t n = 0;
while (pred_begin != pred_end) {
double diff = *pred_begin++ - *true_begin++;
sum += diff * diff;
++n;
}
return sum / n;
}
24. 数学函数在图形学中的应用
24.1 向量运算实现
cpp复制struct Vec3 {
float x, y, z;
Vec3 cross(const Vec3& other) const {
return {
y * other.z - z * other.y,
z * other.x - x * other.z,
x * other.y - y * other.x
};
}
float dot(const Vec3& other) const {
return x * other.x + y * other.y + z * other.z;
}
};
24.2 矩阵变换
cpp复制struct Mat4 {
float m[4][4];
static Mat4 perspective(float fov, float aspect, float near, float far) {
float tanHalfFov = std::tan(fov / 2);
Mat4 result{};
result.m[0][0] = 1 / (aspect * tanHalfFov);
result.m[1][1] = 1 / tanHalfFov;
result.m[2][2] = -(far + near) / (far - near);
result.m[2][3] = -1;
result.m[3][2] = -2 * far * near / (far - near);
return result;
}
};
25. 数学函数在物理引擎中的关键作用
25.1 碰撞检测
cpp复制bool sphereSphereCollision(
const Vec3& pos1, float r1,
const Vec3& pos2, float r2)
{
Vec3 diff = pos1 - pos2;
float distSq = diff.dot(diff);
float radiusSum = r1 + r2;
return distSq <= radiusSum * radiusSum;
}
25.2 刚体运动
cpp复制void integrate(RigidBody& body, float dt) {
body.velocity += body.force * body.invMass * dt;
body.position += body.velocity * dt;
Vec3 angularAccel = body.invInertia * body.torque;
body.angularVelocity += angularAccel * dt;
// 更新旋转
float angle = body.angularVelocity.length() * dt;
if (angle > 0) {
Vec3 axis = body.angularVelocity.normalized();
Quat rotation(axis, angle);
body.orientation = rotation * body.orientation;
}
}
26. 数学函数在音频处理中的应用
26.1 波形生成
cpp复制void generateSineWave(float* buffer, size_t size, float freq, float sampleRate) {
float phase = 0;
float phaseIncr = 2 * std::numbers::pi_v<float> * freq / sampleRate;
for (size_t i = 0; i < size; ++i) {
buffer[i] = std::sin(phase);
phase += phaseIncr;
if (phase >= 2 * std::numbers::pi_v<float>) {
phase -= 2 * std::numbers::pi_v<float>;
}
}
}
26.2 傅里叶变换
cpp复制void dft(const float* input, std::complex<float>* output, size_t n) {
for (size_t k = 0; k < n; ++k) {
std::complex<float> sum(0, 0);
for (size_t t = 0; t < n; ++t) {
float angle = 2 * std::numbers::pi_v<float> * k * t / n;
sum += input[t] * std::exp(std::complex<float>(0, -angle));
}
output[k] = sum;
}
}
27. 数学函数在密码学中的应用
27.1 模幂运算
cpp复制uint64_t modExp(uint64_t base, uint64_t exp, uint64_t mod) {
uint64_t result = 1;
base %= mod;
while (exp > 0) {
if (exp % 2 == 1) {
result = (result * base) % mod;
}
base = (base * base) % mod;
exp >>= 1;
}
return result;
}
27.2 素数检测
cpp复制bool isPrime(uint64_t n, int k = 5) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0) return false;
uint64_t d = n - 1;
while (d % 2 == 0) d /= 2;
for (int i = 0; i < k; ++i) {
uint64_t a = 2 + rand() % (n - 3);
uint64_t x = modExp(a, d, n);
if (x == 1 || x == n - 1) continue;
bool composite = true;
uint64_t temp = d;
while (temp != n - 1) {
x = (x * x) % n;
temp *= 2;
if (x == 1) return false;
if (x == n - 1) {
composite = false;
break;
}
}
if (composite) return false;
}
return true;
}
28. 数学函数在金融计算中的应用
28.1 期权定价
cpp复制double blackScholes(double S, double K, double T, double r, double sigma, char type) {
double d1 = (std::log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * std::sqrt(T));
double d2 = d1 - sigma * std::sqrt(T);
if (type == 'C') {
return S * normalCDF(d1) - K * std::exp(-r * T) * normalCDF(d2);
} else {
return K * std::exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1);
}
}
28.2 复利计算
cpp复制double compoundInterest(double P, double r, int n, double t) {
return P * std::pow(1 + r / n, n * t);
}
29. 数学函数在信号处理中的应用
29.1 数字滤波器
cpp复制class IIRFilter {
std::vector<double> a, b;
std::vector<double> x_hist, y_hist;
public:
IIRFilter(const std::vector<double>& a_coeffs, const std::vector<double>& b_coeffs)
: a(a_coeffs), b(b_coeffs),
x_hist(b_coeffs.size()),
y_hist(a_coeffs.size()) {}
double process(double x) {
// 更新输入历史
std::rotate(x_hist.rbegin(), x_hist.rbegin() + 1, x_hist.rend());
x_hist[0] = x;
// 计算输出
double y = 0;
for (size_t i = 0; i < b.size(); ++i) {
y += b[i] * x_hist[i];
}
for (size_t i = 1; i < a.size(); ++i) {
y -= a[i] * y_hist[i-1];
}
y /= a[0];
// 更新输出历史
std::rotate(y_hist.rbegin(), y_hist.rbegin() + 1, y_hist.rend());
y_hist[0] = y;
return y;
}
};
29.2 窗函数应用
cpp复制void applyHammingWindow(float* signal, size_t n) {
for (size_t i = 0; i < n; ++i) {
float window = 0.54f - 0.46f * std::cos(2 * std::numbers::pi_v<float> * i / (n - 1));
signal[i] *= window;
}
}
30. 数学函数在计算机视觉中的应用
30.1 图像卷积
cpp复制void convolve2D(const float* input, float* output, int width, int height,
const float* kernel, int kernelSize) {
int pad = kernelSize / 2;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
float sum = 0;
for (int ky = 0; ky < kernelSize; ++ky) {
for (int kx = 0; kx < kernelSize; ++kx) {
int ix = x + kx - pad;
int iy = y + ky - pad;
if (ix >= 0 && ix < width && iy >= 0 && iy < height) {
sum += input[iy * width + ix] *
kernel[ky * kernelSize + kx];
}
}
}
output[y * width + x] = sum;
}
}
}
30.2 特征点检测
cpp复制struct KeyPoint {
int x, y;
float response;
};
std::vector<KeyPoint> detectHarrisCorners(const float* image, int width, int height,
float k = 0.04f, float threshold = 0.01f) {
std::vector<float> Ix(width * height);
std::vector<float> Iy(width * height);
// 计算梯度
for (int y = 1; y < height - 1; ++y) {
for (int x = 1; x < width - 1; ++x) {
Ix[y * width + x] = image[y * width + x + 1] - image[y * width + x - 1];
Iy[y * width + x] = image[(y + 1) * width + x] - image[(y - 1) * width + x];
}
}
std::vector<KeyPoint> corners;
for (int y = 1; y < height - 1; ++y) {
for (int x = 1; x < width - 1; ++x) {
// 计算结构张量
float Ixx = 0, Ixy = 0, Iyy = 0;
for (int dy = -1; dy <= 1; ++dy) {
for (int dx = -1; dx <= 1; ++dx) {
int idx = (y + dy) * width + (x + dx);
Ixx += Ix[idx] * Ix[idx];
Ixy += Ix[idx] * Iy[idx];
Iyy += Iy[idx] * Iy[idx];
}
}
// 计算响应函数
float det = Ixx * Iyy - Ixy * Ixy;
float trace = Ixx + Iyy;
float response = det - k * trace * trace;
if (response > threshold) {
corners.push_back({x, y, response});
}
}
}
return corners;
}