From 159504ea2ca060c79ad778fbed7556697dfd7a3c Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sat, 16 May 2026 09:59:51 -0400 Subject: [PATCH] fix min/max variadic fold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fold expression `(r = std::min(arg0, args)), ...` compared `arg0` against each successive `args` element instead of folding through `r`, so for three or more arguments the result was `min(arg0, last_arg)` rather than the true minimum. e.g. `min(5, 3, 7)` returned 5. The single-argument overload (empty parameter pack) returned `r` without ever assigning it — undefined behavior. Initialize `r = arg0` and fold through `r`. Same fix for `maxv`. --- src/stdfunctions.cpp | 8 ++++---- tests/functions.cpp | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/stdfunctions.cpp b/src/stdfunctions.cpp index d452dfb..9934138 100644 --- a/src/stdfunctions.cpp +++ b/src/stdfunctions.cpp @@ -15,16 +15,16 @@ float sign( float x ) template float minv( float arg0, Args... args ) { - float r; - ( ( r = std::min( arg0, args ) ), ... ); + float r = arg0; + ( ( r = std::min( r, args ) ), ... ); return r; } template float maxv( float arg0, Args... args ) { - float r; - ( ( r = std::max( arg0, args ) ), ... ); + float r = arg0; + ( ( r = std::max( r, args ) ), ... ); return r; } diff --git a/tests/functions.cpp b/tests/functions.cpp index ff40d18..cf39c47 100644 --- a/tests/functions.cpp +++ b/tests/functions.cpp @@ -191,3 +191,24 @@ TEST( Functions, CanUseStandardFunctions ) ASSERT_EQ( 1, TestEval( "cos(0)" ) ); ASSERT_EQ( 3, TestEval( "max(1, 2, 3)" ) ); } + +TEST( Functions, MinMaxFoldThroughAccumulator ) +{ + // Smallest/largest is neither the first nor the last argument: a + // buggy fold that compares each tail element against arg0 (rather + // than folding through the running result) would return the wrong + // answer here. + ASSERT_EQ( 3, TestEval( "min(5, 3, 7)" ) ); + ASSERT_EQ( 9, TestEval( "max(1, 9, 4)" ) ); + ASSERT_EQ( 1, TestEval( "min(5, 1, 5, 5, 5)" ) ); + ASSERT_EQ( 9, TestEval( "max(2, 9, 2, 2, 2)" ) ); +} + +TEST( Functions, MinMaxSingleArgument ) +{ + // One-arg form: a fold over an empty parameter pack must still + // produce a defined result equal to that single argument. + ASSERT_EQ( 7, TestEval( "min(7)" ) ); + ASSERT_EQ( 7, TestEval( "max(7)" ) ); + ASSERT_EQ( -3, TestEval( "min(-3)" ) ); +}