blob: 80f701e6089e409709dd6704707f5e151179a790 [file]
#include "tlbmc/thermal/controller/optimizer/pid/frequency_response_estimation/utils.h"
#include <cmath>
#include <cstdlib>
#include <limits>
namespace milotic_tlbmc {
namespace thermal {
namespace optimizer {
double SafeHypotenuse(double u0, double u1) {
double a = std::abs(u0);
double b = std::abs(u1);
if (a < b) {
a /= b;
return std::sqrt(a * a + 1.0) * b;
}
if (a > b) {
b /= a;
return std::sqrt(b * b + 1.0) * a;
}
return std::isnan(b) ? std::numeric_limits<double>::quiet_NaN()
: std::sqrt(2.0) * a;
}
double SafePow(double base, double exp) {
if (std::isnan(base) || std::isnan(exp)) {
return std::numeric_limits<double>::quiet_NaN();
}
double abs_base = std::abs(base);
// Handle `\infty`
if (std::isinf(exp)) {
// `1 ^ {\infty}` is undefined without a very specific mathematical
// context. Examples below:
// 1. `\lim_{n \to \infty} (1 + \frac{1}{n})^n = e`
// 2. `\lim_{n \to \infty} (1 + \frac{1}{\sqrt{n}})^n = \infty`
// 3. `\lim_{n \to \infty} (1 + \frac{k}{n})^n = e^{k}`
if (std::abs(abs_base - 1.0) < kEps) {
return std::numeric_limits<double>::quiet_NaN();
}
if (abs_base > 1.0) {
return exp > 0.0 ? std::numeric_limits<double>::infinity() : 0.0;
}
return exp > 0.0 ? 0.0 : std::numeric_limits<double>::infinity();
}
// `b^0 = 1`
if (std::abs(exp) < kEps) {
return 1.0;
}
// `b^1 = b`, `b^{-1} = 1/b`
double abs_exp = std::abs(exp);
if (std::abs(abs_exp - 1.0) < kEps) {
return exp > 0.0 ? base : 1.0 / base;
}
// `b^2 = b * b`
if (std::abs(exp - 2.0) < kEps) {
return base * base;
}
// `b^{0.5} = \sqrt{b}`
if (std::abs(exp - 0.5) < kEps && base >= 0.0) {
return std::sqrt(base);
}
// A domain error occurs in `std::pow` if the base is negative and the
// exp is fractional.
return (base < 0.0 && exp != std::floor(exp))
? std::numeric_limits<double>::quiet_NaN()
: std::pow(base, exp);
}
} // namespace optimizer
} // namespace thermal
} // namespace milotic_tlbmc