High-performance universal sign function for Python
Project description
csignum-fast
A versatile, high-performance C++ implementation of the universal sign function for Python.
Released on January 1, 2026 ❄️ New Year Edition.
Version 1.1.1/2 corrected links and typos in documentation after a major internal refactor in v1.1.0.
Key Features
- Uniform Results: Always returns only
-1,0, or1as anintfor valid numeric comparisons. - Correct Edge Case Handling:
sign(+0.0)andsign(-0.0)return0.sign(inf)returns1,sign(-inf)returns-1.- For any NaN (float NaN, Decimal NaN, etc.), it returns
math.nan(float).
- Comprehensive Duck Typing: Delegates comparisons to the argument's class. Works seamlessly with:
- Built-in
int(including arbitrary-precision),bool, andfloat. fractions.Fractionanddecimal.Decimal.- Any existing and future objects that support rich comparisons with numbers.
- Built-in
- Informative Error Handling for Easy Debugging: Provides clear, descriptive
TypeErrormessages when passed non-numeric, non-scalar, or incomparable arguments. - ⚡ High Performance: Branch-optimized C++20 core.
- ✅ Thoroughly Tested: Tested on 92 cases including different types, edge cases, new custom class, and inappropriate arguments. Also tested memory leaks and benchmarking against v1.0.2.
- ✨ Pre-processing Engine: Use the
preprocesskeyword argument to transform input before calculation or trigger an "Early Exit" (recursion permitted). - 🛡️ Exception safety: The
if_exckeyword argument allows you to define a fallback value (likeNone,math.nan, or-2) instead of crashing on invalid types.
Installation
pip install csignum-fast
Standard Usage
from signum import sign
from decimal import Decimal
print(sign(-10**100)) # -1
print(sign(3.14)) # 1
print(sign(Decimal("0.0"))) # 0
print(sign(float('-nan'))) # nan
Advanced Usage (New features since v1.1.0)
☢️ Attention: Contract Programming!
For productivity reasons, keyword argument values are not checked by the sign function. It is your responsibility to:
- Pass a
callablewith one argument forpreprocess(must returnNoneor atuple). - Pass a
tupleforif_exc.
Passing incorrect types to these parameters may lead to undefined behavior or faults.
⚡ Custom Pre-processing with preprocess
You can pass a callable to transform the input. The argument of callable is the positional argument of sign. The callable should support a special return protocol:
- Return
None: Proceed with usual calculation. - Return
(value,): Proceed with calculation usingvalueas an argument. Why atuple? Use(None,)to returnNoneas avalue. - Return
(any, result): Early Exit. Immediately returnresultas the final answer.anyis ignored.
from signum import sign
from decimal import Decimal
from fractions import Fraction
from math import nan, inf
import re
# Convert str to float; uses lambda as callable
sign('5.0', preprocess=lambda a: (float(a),)) # Returns 1 (instead of exception)
# Treat small number as zero through argument replacement only
EPS = 1e-9
sign(-.187e-17, preprocess=lambda a: (0 if abs(a) < EPS else a,)) # Returns 0 (instead of -1)
# Treat small number as zero through argument or result replacement; uses variable as callable
ppf1 = lambda x: (x, 0) if abs(x) < EPS else (x,)
sign(-.187e-17, preprocess=ppf1) # Returns 0 (instead of -1)
# Extract number from string, replace only string argument; supplies function as callable
numeric_finder = re.compile(r"[-+]?(?:\d+\.\d*|\.\d+|\d+)(?:[eE][-+]?\d+)?")
def n_extract(s):
if isinstance(s, str):
match = numeric_finder.search(s)
return (float(match.group()),) if match else None
return None
sign("☠️ 15 men on the dead man's chest☠️", preprocess=n_extract) # Returns sign(15) == 1
# Do you want sign(complex) instead of exception?
def c_prep(z):
if z == 0 or not isinstance(z, complex): return None
# complex z != 0
return (0, z/abs(z))
sign(-1+1j, preprocess=c_prep) # Returns (-0.7071067811865475+0.7071067811865475j)
# numpy flavor: float result for float or Decimal argument; uses recursive call of sign
ppf2 = lambda a: (a, float(sign(a))) if isinstance(a, (float, Decimal)) else None
sign(-5.0, preprocess=ppf2) # Returns -1.0 (instead of -1)
🛡️ Exception Safety with if_exc
With this keyword, you can avoid try-except blocks. If sign() encounters an incompatible type, it will return your fallback value instead of raising a TypeError. if_exc should be a tuple that permits you to pass None as the fallback value through if_exc=(None,). (Default if_exc=None is totally different).
import math
from signum import sign
# Returns -2 instead of crashing
res = sign("not a number", if_exc=(-2,))
You can use both keyword arguments at once
With preprocess, you replace arguments (or results) in specific cases, while if_exc prevents exceptions for all that remains.
📊 Performance & Quality Assurance
Benchmark Results
Versions 1.1.0/1 maintain near-zero overhead (+0.8% latency) despite adding logic for new arguments. See details in the "Benchmarking" section in README for tests.
Reliability
- Memory Safety: Verified with long-run leak tests (0 bytes leaked over 4M iterations).
- Test Coverage: 92 validation cases (up from 51 in v1.0.2).
License
This project is licensed under the MIT License. See the LICENSE file for details.
Author
Alexandru Colesnicov: GitHub Profile
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file csignum_fast-1.1.2.tar.gz.
File metadata
- Download URL: csignum_fast-1.1.2.tar.gz
- Upload date:
- Size: 17.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
283babedc523c8b539465f9f1055367699c9a8be77e84797363c2d6ad7e600e5
|
|
| MD5 |
7c8d151f81847e35307c1e6c89cf3073
|
|
| BLAKE2b-256 |
fb779078de347001c83c4b4e81edb0b1e4cb9dd6a71b0c14d8740fe11a643378
|
File details
Details for the file csignum_fast-1.1.2-cp313-cp313-win_amd64.whl.
File metadata
- Download URL: csignum_fast-1.1.2-cp313-cp313-win_amd64.whl
- Upload date:
- Size: 11.3 kB
- Tags: CPython 3.13, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
252346aea3d840586c1c3fd75ec4aab0265776a8406e575d60426e878818e94f
|
|
| MD5 |
b9335d56951bfa5e0668ad2b02a7a4be
|
|
| BLAKE2b-256 |
a589197eff12e0350e504498b5d2ad8ab7ba1e293edfa3d0d4b455ffeb883955
|