This repository is based on the official DuckDB
extension-template:
https://github.com/duckdb/extension-template
If you’re interested in building and distributing your own DuckDB extension, that repository is a great starting point.
The Clamp extension introduces range-clamping scalar functions to DuckDB. Initial development focuses on saturation arithmetic, with future plans to expand into additional math-based functions.
It currently provides:
clamp(value, min, max)— clamp to an arbitrary rangeclip(value, min, max)- alias forclampsaturate(value)— clamp to the[0, 1]rangeclamp01(value)— alias forsaturate, common in graphics and MLwrap(value, min, max)- Wrap a value x into the range [min_val, max_val) using modular arithmeticpingpong(value, min, max)- Return a value that "bounces" back and forth between the minimum and maximumfract(value)- Return the fractional (decimal) part of a number
All functions are implemented as native DuckDB scalar functions with vectorized execution for high performance.
- Native DuckDB scalar functions (
ScalarFunctionSet) - Vectorized execution via DuckDB executors
TernaryExecutorforclamp/clip/wrap/pingpongUnaryExecutorforsaturate/clamp01/fract
- Supports:
BIGINT(int64_t) —clamp,saturate1,clamp01,clip,wrap,pingpong,fractDOUBLE—clamp,saturate,clamp01,clip,wrap,pingpong,fract
- NULL-safe:
- returns
NULLif any input argument isNULL
- returns
- Strict validation:
clamp/clipthrows an error ifmin > maxwrapthrows an error ifmin >= maxpingpongthrows an error ifmin>=max
- Build the extension using DuckDB’s extension build system (see Building below).
- Place the compiled
clamp.duckdb_extensionbinary in DuckDB’s extension directory or load it explicitly.
DuckDB extensions currently rely on DuckDB's build system to provide easy testing and distributing. This does however come at the downside of requiring the template to build DuckDB and its unittest binary every time you build your extension. To mitigate this, we highly recommend installing ccache and ninja. This will ensure you only need to build core DuckDB once and allows for rapid rebuilds.
To clean build using ninja and ccache ensure both are installed and run:
rm -rf build
GEN=ninja makeTo load the extension:
LOAD 'build/release/extension/clamp/clamp.duckdb_extension';
SELECT clamp(15, 10, 20);Load the extension:
INSTALL clamp;
LOAD clamp;Restricts a value to an inclusive minimum and maximum bound.
-- Value within bounds
SELECT clamp(15, 10, 20);
-- 15
-- Below minimum
SELECT clamp(5, 10, 20);
-- 10
-- Above maximum
SELECT clamp(25, 10, 20);
-- 20
-- Floating point support
SELECT clamp(3.14, 2.71, 4.0);
-- 3.14
-- NULL propagation
SELECT clamp(NULL, 10, 20);
-- NULLIf the minimum bound is greater than the maximum bound, the function throws an error:
SELECT clamp(15, 20, 10);
-- CLAMP error: Minimum bound (20) cannot be greater than maximum bound (10).Alias for clamp - restricts a value to an inclusive minimum and maximum bound.
-- Value within bounds
SELECT clip(15, 10, 20);
-- 15
-- Below minimum
SELECT clip(5, 10, 20);
-- 10
-- Above maximum
SELECT clip(25, 10, 20);
-- 20A specialized clamp that restricts a value to the [0, 1] range.
This function is commonly used in graphics, normalization, and machine learning workflows.
SELECT saturate(-0.25);
-- 0.0
SELECT saturate(0.5);
-- 0.5
SELECT saturate(1.75);
-- 1.0
SELECT saturate(NULL);
-- NULLAn alias for saturate(value).
This name is widely used in graphics and shader languages and is provided for familiarity and readability.
SELECT clamp01(-1.0);
-- 0.0
SELECT clamp01(0.25);
-- 0.25
SELECT clamp01(2.0);
-- 1.0Wrap a value x into the range [min_val, max_val) using modular arithmetic.
Definition:
WRAP(x, min_val, max_val) = min_val + ((x - min_val) % (max_val - min_val))
SELECT wrap(11, 0, 10);
-- 1
SELECT wrap(25, 10, 20);
--15
SELECT wrap(45, 10, 20);
--15
SELECT wrap(5, 10, 20);
--15
SELECT wrap(-15, 10, 20);
--15
SELECT wrap(370, 0, 360), wrap(-10, 0, 360), wrap(720, 0, 360);
-- 10.0 350.0 0.0Returns a value that "bounces" back and forth between the minimum and maximum boundaries. This creates a triangle wave pattern, commonly used for smooth oscillations in animations and procedural generation.
When value is at min, the result is min. As value increases toward
max, the result increases linearly. Upon hitting max, the result reverses
direction and decreases back toward min. The output is periodic with a full
"round-trip" period of 2 * (max-min).
SELECT pingpong(11, 0, 10);
-- 9
-- Oscillation between 10 and 20
SELECT pingpong(12, 10, 20);
-- 12.0
-- 2 past the max (20), so it bounces back to 18
SELECT pingpong(22, 10, 20);
-- 18.0
-- 8 past the max (20), so it bounces back to 12
SELECT pingpong(28, 10, 20);
-- 12.0Returns the fractional (decimal) part of a number. This is defined as x - floor(x).
Isolates the digits following the decimal point. For negative numbers,
fract returns the positive remainder required to reach the next
lower integer.
-- Standard use
SELECT fract(1.75);
-- 0.75
-- Integer inputs always return 0
SELECT fract(10);
-- 0.0
-- Negative wrapping (Blender/Shader style)
SELECT fract(-0.1);
-- 0.9
clamp(value, min, max)
clip(value, min, max)
saturate(value)
clamp01(value)
wrap(value, min, max)
pingpong(value, min, max)
fract(value)
-
valueThe value to restrict. -
minLower bound (inclusive). Used only byclamp. -
maxUpper bound (inclusive). Used only byclamp.
All arguments must be of the same type.
| Function | BIGINT | DOUBLE |
|---|---|---|
| clamp | ✓ | ✓ |
| saturate | ✓ | ✓ |
| clamp01 | ✓ | ✓ |
| clip | ✓ | ✓ |
| wrap | ✓ | ✓ |
| pingpong | ✓ | ✓ |
| fract | ✓ | ✓ |
- The core clamp logic is implemented in
ClampOperator::Operation<T>. saturateandclamp01use a specialized unary operator with fixed bounds for efficiency and clarity.- DuckDB’s vectorized executors apply operations across data chunks, enabling efficient batch execution.
- NULL handling uses DuckDB’s
DEFAULT_NULL_HANDLING, ensuring standard SQL semantics.
SQL tests live in:
test/sql/clamp.test
Run the test suite with:
make testWhile this extension currently provides clamp, clip, wrap, saturate, and
others, it intentionally leaves room for other mathematically
range-based utilities that frequently appear in numerical computing,
analytics, and graphics domains.
Possible future additions include:
-
IsPowerOfTwo Boolean check that returns
trueif a number is a power of two. -
DeltaAngle Calculates the shortest distance between two angles.
These functions share similar characteristics:
- Simple scalar logic
- Deterministic and vectorizable execution
- No external dependencies
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Copyright (c) 2026
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.