From 59d769bf1823c4196a8f292b22d19fedd70ad92e Mon Sep 17 00:00:00 2001 From: Alwin Garside Date: Fri, 31 Jan 2025 22:00:55 +0100 Subject: [PATCH] feat: add is_zero() --- src/constants.php | 5 +++++ src/functions.php | 13 +++++++++++++ tests/Unit/FunctionsTest.php | 25 ++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/constants.php b/src/constants.php index 8559939..853553f 100644 --- a/src/constants.php +++ b/src/constants.php @@ -15,3 +15,8 @@ * This mask matches every possible past and future PHP error level. */ const E_EVERYTHING = 0x7FFFFFFF; + +/** + * Default error tolerance. + */ +const PHP_ZERO_TOLERANCE = 0.00000000001; diff --git a/src/functions.php b/src/functions.php index 10b0b35..84ae230 100644 --- a/src/functions.php +++ b/src/functions.php @@ -56,6 +56,19 @@ function is_closed_resource(mixed $value): bool return Type::ClosedResource->is($value); } +/** + * Finds whether the given number is (sufficiently close to) 0. + * + * @param int|float $value The number being evaluated. + * @param float|null $tolerance Tolerance allowed when evaluating the number. + * @return bool Returns true if **value** is (sufficiently close to) 0, + * false otherwise. + */ +function is_zero(int | float $value, ?float $tolerance = PHP_ZERO_TOLERANCE): bool +{ + return 0 === $value || 0.0 === $value || (null !== $tolerance && \abs($value) <= $tolerance); +} + /** * Sequences a value into a {@see \Generator}. * diff --git a/tests/Unit/FunctionsTest.php b/tests/Unit/FunctionsTest.php index 2c780f9..8544a0b 100644 --- a/tests/Unit/FunctionsTest.php +++ b/tests/Unit/FunctionsTest.php @@ -54,7 +54,6 @@ ]); }); - describe('is_closed_resource()', function () { it('correctly returns whether value is a closed resource', function ($value, $type) { $isClosed = rephine\is_closed_resource($value); @@ -64,6 +63,30 @@ })->with('types / test cases'); }); +describe('is_zero()', function () { + test('returns true if value is (close to) 0', function ($value, $tolerance) { + $isZero = rephine\is_zero($value, $tolerance); + + expect($isZero)->toBeTrue(); + })->with([ + [0, null], + [0.0, null], + [PHP_FLOAT_MIN, rephine\PHP_ZERO_TOLERANCE], + [PHP_FLOAT_MIN, PHP_FLOAT_MIN], + ]); + + test('returns false if value is not (close to) 0', function ($value, $tolerance) { + $isZero = rephine\is_zero($value, $tolerance); + + expect($isZero)->toBeFalse(); + })->with([ + [1, rephine\PHP_ZERO_TOLERANCE], + [1.0, rephine\PHP_ZERO_TOLERANCE], + [PHP_FLOAT_EPSILON, PHP_FLOAT_MIN], + [PHP_FLOAT_MIN, null], + ]); +}); + describe('seq()', function () { test('properly sequences values', function ($data, $expected) { $array = [];