From 7af7f95509dd9cc6e42ebd541747b96660e21327 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 23 Jul 2026 14:17:31 -0400 Subject: [PATCH] fix: swapped modf outputs in circular variable::value() std::modf returns the fractional part and stores the whole part in its output argument, but the circular branch of variable::value() used the return value as the bin index and the whole part as the interpolation weight. The error cancels for size-2 axes with integer arguments, which is all the old test covered. Assisted-by: ClaudeCode:claude-fable-5 --- include/boost/histogram/axis/variable.hpp | 5 +++-- test/axis_variable_test.cpp | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/include/boost/histogram/axis/variable.hpp b/include/boost/histogram/axis/variable.hpp index 6be785e7..45b79345 100644 --- a/include/boost/histogram/axis/variable.hpp +++ b/include/boost/histogram/axis/variable.hpp @@ -210,8 +210,9 @@ class variable : public iterator_mixin(std::modf(i, &z)); + double whole; + const double z = std::modf(i, &whole); + const auto k = static_cast(whole); const auto a = vec_[0]; const auto b = vec_[size()]; return (1.0 - z) * vec_[k] + z * vec_[k + 1] + shift * (b - a); diff --git a/test/axis_variable_test.cpp b/test/axis_variable_test.cpp index 1216bb77..2f938144 100644 --- a/test/axis_variable_test.cpp +++ b/test/axis_variable_test.cpp @@ -123,6 +123,21 @@ int main() { BOOST_TEST_EQ(a.index(4), 1); // 4 - 3 = 1 } + // axis::variable circular with size > 2 and non-equidistant bins; + // the error in value() cancels for size == 2 and integer arguments + { + axis::variable a{0, 1, 3, 4}; + BOOST_TEST_EQ(a.value(-1), -1); // vec_[2] - period + BOOST_TEST_EQ(a.value(0), 0); + BOOST_TEST_EQ(a.value(1), 1); + BOOST_TEST_EQ(a.value(2), 3); + BOOST_TEST_EQ(a.value(3), 4); + BOOST_TEST_EQ(a.value(4), 5); + BOOST_TEST_EQ(a.value(0.5), 0.5); + BOOST_TEST_EQ(a.value(1.5), 2); + BOOST_TEST_EQ(a.value(2.5), 3.5); + } + // axis::regular with growth { using pii_t = std::pair;