[sensor] Replace powf with pow10_int in sensor filters

Add shared pow10_int() helper to helpers.h that computes 10^exp
using iterative multiplication/division instead of powf.

Replace powf(10, exp) calls in:
- sensor/filter.cpp: ValueListFilter and RoundFilter
- helpers.cpp: normalize_accuracy_decimals (refactored to use
  pow10_int for the general case, keeping -1/-2 fast paths)

Eliminates powf/__ieee754_powf from builds where sensor filters
are the only remaining powf call site.
This commit is contained in:
J. Nick Koston
2026-02-19 15:28:12 -06:00
parent d2b7d40716
commit 6a8844e2fc
3 changed files with 25 additions and 14 deletions

View File

@@ -2,6 +2,7 @@
#include <cmath>
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "sensor.h"
@@ -237,7 +238,7 @@ ValueListFilter::ValueListFilter(std::initializer_list<TemplatableValue<float>>
bool ValueListFilter::value_matches_any_(float sensor_value) {
int8_t accuracy = this->parent_->get_accuracy_decimals();
float accuracy_mult = powf(10.0f, accuracy);
float accuracy_mult = pow10_int(accuracy);
float rounded_sensor = roundf(accuracy_mult * sensor_value);
for (auto &filter_value : this->values_) {
@@ -469,7 +470,7 @@ optional<float> ClampFilter::new_value(float value) {
RoundFilter::RoundFilter(uint8_t precision) : precision_(precision) {}
optional<float> RoundFilter::new_value(float value) {
if (std::isfinite(value)) {
float accuracy_mult = powf(10.0f, this->precision_);
float accuracy_mult = pow10_int(this->precision_);
return roundf(accuracy_mult * value) / accuracy_mult;
}
return value;

View File

@@ -468,20 +468,15 @@ ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) {
static inline void normalize_accuracy_decimals(float &value, int8_t &accuracy_decimals) {
if (accuracy_decimals < 0) {
// Clamp to -9 to keep divisor within uint32_t range (max 10^9 = 1,000,000,000)
int8_t dec = accuracy_decimals < -9 ? (int8_t) -9 : accuracy_decimals;
uint32_t divisor;
if (dec == -1) {
divisor = 10;
} else if (dec == -2) {
divisor = 100;
float divisor;
if (accuracy_decimals == -1) {
divisor = 10.0f;
} else if (accuracy_decimals == -2) {
divisor = 100.0f;
} else {
divisor = 1000;
for (int8_t i = dec + 3; i < 0; i++)
divisor *= 10;
divisor = pow10_int(-accuracy_decimals);
}
auto divisor_f = static_cast<float>(divisor);
value = roundf(value / divisor_f) * divisor_f;
value = roundf(value / divisor) * divisor;
accuracy_decimals = 0;
}
}

View File

@@ -439,6 +439,21 @@ template<size_t STACK_SIZE, typename T = uint8_t> class SmallBufferWithHeapFallb
/// @name Mathematics
///@{
/// Compute 10^exp using iterative multiplication/division.
/// Avoids pulling in powf/__ieee754_powf (~2.3KB flash) for small integer exponents.
/// Exact for non-negative exponents up to 10 (powers of 10 up to 10^10 are exact in float).
inline float pow10_int(int8_t exp) {
float result = 1.0f;
if (exp >= 0) {
for (int8_t i = 0; i < exp; i++)
result *= 10.0f;
} else {
for (int8_t i = exp; i < 0; i++)
result /= 10.0f;
}
return result;
}
/// Remap \p value from the range (\p min, \p max) to (\p min_out, \p max_out).
template<typename T, typename U> T remap(U value, U min, U max, T min_out, T max_out) {
return (value - min) * (max_out - min_out) / (max - min) + min_out;