]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Fix] Use proper rounding for symbol frequency statistics 5840/head
authorAlexander Moisseev <moiseev@mezonplus.ru>
Wed, 14 Jan 2026 16:11:50 +0000 (19:11 +0300)
committerAlexander Moisseev <moiseev@mezonplus.ru>
Wed, 14 Jan 2026 17:09:05 +0000 (20:09 +0300)
- Replace incorrect floor() with round() in rounding functions to avoid
  losing small values
- Increase counters API frequency precision from 3 to 6 decimal places
  (need 5 to avoid rspamc displaying values as multiples of 0.06, need 6
  for /counters endpoint itself - no additional overhead as JSON stores
  double anyway)
- Add frequency_stddev field to counters API output (fixes zero stdev in
  `rspamc counters` output)
- Clarify `rspamc counters` table header with "avg (stddev)" subheading
- Fix WebUI to preserve frequency precision before scaling

Example for symbol with frequency 0.004772 hits/sec:
- Before: /symbols returns 0.004772, /counters returns 0.004000,
  `rspamc counters` shows 0.240
- After:  /symbols returns 0.004772, /counters returns 0.004772,
  `rspamc counters` shows 0.286

interface/js/app/symbols.js
src/client/rspamc.cxx
src/libserver/symcache/symcache_impl.cxx

index 540eaa6e311c8fcf50cf28bf7ce7868a4ef3fb73..576547b75e71f3b8df2bc5d7b34a0820b07b56d9 100644 (file)
@@ -68,7 +68,8 @@ define(["jquery", "app/common", "footable"],
                         item.frequency = 0;
                     }
                     freqs.push(item.frequency);
-                    item.frequency = Number(item.frequency).toFixed(2);
+                    // Don't round yet, keep precision for scaling
+                    item.frequency = Number(item.frequency);
                     if (!(item.group in lookup)) {
                         lookup[item.group] = 1;
                         distinct_groups.push(item.group);
index af3a13f0a550ba9fb319ef9bd1762880b82002bd..369c6b31effad72ce2bd7574e2c40d0ff3493a4e 100644 (file)
@@ -1562,6 +1562,9 @@ rspamc_counters_output(FILE *out, ucl_object_t *obj)
                                 "Frequency",
                                 "Hits");
        rspamc_print(out, " {} \n", emphasis_argument(dash_buf));
+       rspamc_print(out, "| {:<4} | {:<{}} | {:^7} | {:^13} | {:^7} |\n", "",
+                                "", max_len,
+                                "", "avg (stddev)", "");
        rspamc_print(out, "| {:<4} | {:<{}} | {:^7} | {:^13} | {:^7} |\n", "",
                                 "", max_len,
                                 "", "hits/min", "");
index 44b7a4bab14b08d3a189fafc80d2275051b90868..e2be5c40eba243545e877959b7ed148e009a99f8 100644 (file)
@@ -373,7 +373,7 @@ auto symcache::load_items() -> bool
 template<typename T>
 static constexpr auto round_to_hundreds(T x)
 {
-       return (::floor(x) * 100.0) / 100.0;
+       return ::round(x * 100.0) / 100.0;
 }
 
 bool symcache::save_items() const
@@ -1031,7 +1031,7 @@ auto symcache::counters() const -> ucl_object_t *
        auto *top = ucl_object_typed_new(UCL_ARRAY);
        constexpr const auto round_float = [](const auto x, const int digits) -> auto {
                const auto power10 = ::pow(10, digits);
-               return (::floor(x * power10) / power10);
+               return (::round(x * power10) / power10);
        };
 
        for (auto &pair: items_by_symbol) {
@@ -1049,8 +1049,11 @@ auto symcache::counters() const -> ucl_object_t *
                                                                          ucl_object_fromdouble(round_float(item->st->weight, 3)),
                                                                          "weight", 0, false);
                                ucl_object_insert_key(obj,
-                                                                         ucl_object_fromdouble(round_float(parent->st->avg_frequency, 3)),
+                                                                         ucl_object_fromdouble(round_float(parent->st->avg_frequency, 6)),
                                                                          "frequency", 0, false);
+                               ucl_object_insert_key(obj,
+                                                                         ucl_object_fromdouble(round_float(::sqrt(parent->st->stddev_frequency), 6)),
+                                                                         "frequency_stddev", 0, false);
                                ucl_object_insert_key(obj,
                                                                          ucl_object_fromint(parent->st->total_hits),
                                                                          "hits", 0, false);
@@ -1065,6 +1068,9 @@ auto symcache::counters() const -> ucl_object_t *
                                ucl_object_insert_key(obj,
                                                                          ucl_object_fromdouble(0.0),
                                                                          "frequency", 0, false);
+                               ucl_object_insert_key(obj,
+                                                                         ucl_object_fromdouble(0.0),
+                                                                         "frequency_stddev", 0, false);
                                ucl_object_insert_key(obj,
                                                                          ucl_object_fromdouble(0.0),
                                                                          "hits", 0, false);
@@ -1078,8 +1084,11 @@ auto symcache::counters() const -> ucl_object_t *
                                                                  ucl_object_fromdouble(round_float(item->st->weight, 3)),
                                                                  "weight", 0, false);
                        ucl_object_insert_key(obj,
-                                                                 ucl_object_fromdouble(round_float(item->st->avg_frequency, 3)),
+                                                                 ucl_object_fromdouble(round_float(item->st->avg_frequency, 6)),
                                                                  "frequency", 0, false);
+                       ucl_object_insert_key(obj,
+                                                                 ucl_object_fromdouble(round_float(::sqrt(item->st->stddev_frequency), 6)),
+                                                                 "frequency_stddev", 0, false);
                        ucl_object_insert_key(obj,
                                                                  ucl_object_fromint(item->st->total_hits),
                                                                  "hits", 0, false);