Skip to content

Commit

Permalink
multipleOf rounding methods
Browse files Browse the repository at this point in the history
  • Loading branch information
sirn-se committed Aug 2, 2024
1 parent 1caf946 commit 3aec98f
Show file tree
Hide file tree
Showing 12 changed files with 358 additions and 26 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ $numerics->setLocale('sv_SE'); // Set to Swedish

| Version | PHP | |
| --- | --- | --- |
| `2.4` | `^8.0` | tbc |
| `2.4` | `^8.0` | Multiple-of rounding: `mround()` `mfloor()` `mceil()` methods |
| `2.3` | `^7.4\|^8.0` | Precision improvements, negative precision in format() |
| `2.2` | `^7.4\|^8.0` | Default locale |
| `2.1` | `^7.1\|^8.0` | |
Expand Down
44 changes: 44 additions & 0 deletions src/Numerics.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

namespace Phrity\Util;

use DomainException;

/**
* Numerics utility class.
* Float versions of ceil(), floor() and rand() with precision.
Expand Down Expand Up @@ -83,6 +85,48 @@ public function round(float $number, int|null $precision = null): float
return round($number, $precision ?? $this->precision ?? 0);
}

/**
* Floor function using multiple-of.
* @param float $number The number to apply floor to
* @param float $multipleOf Rounding target multiple
* @return float Return floor by multiple-of (null if not solvable)
*/
public function mfloor(float $number, float $multipleOf): float
{
if ($multipleOf <= 0) {
throw new DomainException('Argument #2 ($multipleOf) must be a float higher than 0');
}
return round(floor($number / $multipleOf) * $multipleOf, $this->precision($multipleOf));
}

/**
* Ceil function using multiple-of.
* @param float $number The number to apply ceil to
* @param float $multipleOf Rounding target multiple
* @return float Return ceil by multiple-of (null if not solvable)
*/
public function mceil(float $number, float $multipleOf): float
{
if ($multipleOf <= 0) {
throw new DomainException('Argument #2 ($multipleOf) must be a float higher than 0');
}
return round(ceil($number / $multipleOf) * $multipleOf, $this->precision($multipleOf));
}

/**
* Round function using multiple-of.
* @param float $number The number to apply round to
* @param float $multipleOf Rounding target multiple
* @return float Return round by multiple-of (null if not solvable)
*/
public function mround(float $number, float $multipleOf): float
{
if ($multipleOf <= 0) {
throw new DomainException('Argument #2 ($multipleOf) must be a float higher than 0');
}
return round(round($number / $multipleOf) * $multipleOf, $this->precision($multipleOf));
}

/**
* Random float number generator with precision.
* @param float $min Lowest result
Expand Down
9 changes: 6 additions & 3 deletions tests/CeilTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

namespace Phrity\Util;

use Phrity\Util\Numerics;
use PHPUnit\Framework\TestCase;
use Phrity\Util\Numerics;
use TypeError;

/**
* Numerics ceil test class.
Expand Down Expand Up @@ -99,7 +100,8 @@ public function testInstance(): void
public function testInvalidNumberInput(): void
{
$numerics = new Numerics();
$this->expectException('TypeError');
$this->expectException(TypeError::class);
$this->expectExceptionMessage('Argument #1 ($number) must be of type float, string given');
$numerics->ceil('should fail');
}

Expand All @@ -109,7 +111,8 @@ public function testInvalidNumberInput(): void
public function testInvalidPrecisionInput(): void
{
$numerics = new Numerics();
$this->expectException('TypeError');
$this->expectException(TypeError::class);
$this->expectExceptionMessage('Argument #2 ($precision) must be of type ?int, string given');
$numerics->ceil(12.34, 'should fail');
}
}
9 changes: 6 additions & 3 deletions tests/FloorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

namespace Phrity\Util;

use Phrity\Util\Numerics;
use PHPUnit\Framework\TestCase;
use Phrity\Util\Numerics;
use TypeError;

/**
* Numerics floor test class.
Expand Down Expand Up @@ -99,7 +100,8 @@ public function testInstance(): void
public function testInvalidNumberInput(): void
{
$numerics = new Numerics();
$this->expectException('TypeError');
$this->expectException(TypeError::class);
$this->expectExceptionMessage('Argument #1 ($number) must be of type float, string given');
$numerics->floor('should fail');
}

Expand All @@ -109,7 +111,8 @@ public function testInvalidNumberInput(): void
public function testInvalidPrecisionInput(): void
{
$numerics = new Numerics();
$this->expectException('TypeError');
$this->expectException(TypeError::class);
$this->expectExceptionMessage('Argument #2 ($precision) must be of type ?int, string given');
$numerics->floor(12.34, 'should fail');
}
}
9 changes: 6 additions & 3 deletions tests/FormatTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

namespace Phrity\Util;

use Phrity\Util\Numerics;
use PHPUnit\Framework\TestCase;
use Phrity\Util\Numerics;
use TypeError;

/**
* Numerics format test class.
Expand Down Expand Up @@ -79,7 +80,8 @@ public function testPrecisions(): void
public function testInvalidNumberInput(): void
{
$numerics = new Numerics();
$this->expectException('TypeError');
$this->expectException(TypeError::class);
$this->expectExceptionMessage('Argument #1 ($number) must be of type float, string given');
$numerics->format('should fail');
}

Expand All @@ -89,7 +91,8 @@ public function testInvalidNumberInput(): void
public function testInvalidPrecisionInput(): void
{
$numerics = new Numerics();
$this->expectException('TypeError');
$this->expectException(TypeError::class);
$this->expectExceptionMessage('Argument #2 ($precision) must be of type ?int, string given');
$numerics->format(12.34, 'should fail');
}
}
90 changes: 90 additions & 0 deletions tests/MCeilTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

/**
* File for Numerics ceil function tests.
* @package Phrity > Util > Numerics
*/

declare(strict_types=1);

namespace Phrity\Util;

use DomainException;
use PHPUnit\Framework\TestCase;
use Phrity\Util\Numerics;
use TypeError;

/**
* Numerics mceil test class.
*/
class MCeilTest extends TestCase
{
/**
* Set up for all tests
*/
public function setUp(): void
{
error_reporting(-1);
}

/**
* Test ceil with decimal multiples
*/
public function testCeilDecimal(): void
{
$numerics = new Numerics();

$this->assertEquals(123.123, $numerics->mceil(123.123, 0.001));
$this->assertEquals(123.13, $numerics->mceil(123.123, 0.01));
$this->assertEquals(123.2, $numerics->mceil(123.123, 0.1));
$this->assertEquals(124, $numerics->mceil(123.123, 1));
$this->assertEquals(130, $numerics->mceil(123.123, 10));
$this->assertEquals(200, $numerics->mceil(123.123, 100));
}

/**
* Test ceil with fractions
*/
public function testCeilFractions(): void
{
$numerics = new Numerics();

$this->assertEquals(123.25, $numerics->mceil(123.123, 0.25));
$this->assertEquals(125, $numerics->mceil(123.123, 2.5));
$this->assertEquals(125, $numerics->mceil(123.123, 25));
$this->assertEquals(250, $numerics->mceil(123.123, 250));
}

/**
* Test unresolvable
*/
public function testInvalidMultipleOf(): void
{
$numerics = new Numerics();
$this->expectException(DomainException::class);
$this->expectExceptionMessage('Argument #2 ($multipleOf) must be a float higher than 0');
$this->assertNull($numerics->mceil(456.789, 0));
}

/**
* Test invalid input type on number argument
*/
public function testInvalidNumberInput(): void
{
$numerics = new Numerics();
$this->expectException(TypeError::class);
$this->expectExceptionMessage('Argument #1 ($number) must be of type float, string given');
$numerics->mceil('should fail', 1);
}

/**
* Test invalid input type on multiple-by argument
*/
public function testInvalidMultipleOfInput(): void
{
$numerics = new Numerics();
$this->expectException(TypeError::class);
$this->expectExceptionMessage('Argument #2 ($multipleOf) must be of type float, string given');
$numerics->mceil(12.34, 'should fail');
}
}
90 changes: 90 additions & 0 deletions tests/MFloorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

/**
* File for Numerics mfloor function tests.
* @package Phrity > Util > Numerics
*/

declare(strict_types=1);

namespace Phrity\Util;

use DomainException;
use PHPUnit\Framework\TestCase;
use Phrity\Util\Numerics;
use TypeError;

/**
* Numerics mfloor test class.
*/
class MFloorTest extends TestCase
{
/**
* Set up for all tests
*/
public function setUp(): void
{
error_reporting(-1);
}

/**
* Test floor with decimal multiples
*/
public function testFloorDecimal(): void
{
$numerics = new Numerics();

$this->assertEquals(456.789, $numerics->mfloor(456.789, 0.001));
$this->assertEquals(456.78, $numerics->mfloor(456.789, 0.01));
$this->assertEquals(456.7, $numerics->mfloor(456.789, 0.1));
$this->assertEquals(456, $numerics->mfloor(456.789, 1));
$this->assertEquals(450, $numerics->mfloor(456.789, 10));
$this->assertEquals(400, $numerics->mfloor(456.789, 100));
}

/**
* Test floor with fractions
*/
public function testFloorFractions(): void
{
$numerics = new Numerics();

$this->assertEquals(456.75, $numerics->mfloor(456.789, 0.25));
$this->assertEquals(455, $numerics->mfloor(456.789, 2.5));
$this->assertEquals(450, $numerics->mfloor(456.789, 25));
$this->assertEquals(250, $numerics->mfloor(456.789, 250));
}

/**
* Test unresolvable
*/
public function testInvalidMultipleOf(): void
{
$numerics = new Numerics();
$this->expectException(DomainException::class);
$this->expectExceptionMessage('Argument #2 ($multipleOf) must be a float higher than 0');
$this->assertNull($numerics->mfloor(456.789, 0));
}

/**
* Test invalid input type on number argument
*/
public function testInvalidNumberInput(): void
{
$numerics = new Numerics();
$this->expectException(TypeError::class);
$this->expectExceptionMessage('Argument #1 ($number) must be of type float, string given');
$numerics->mfloor('should fail', 1);
}

/**
* Test invalid input type on precision argument
*/
public function testInvalidMultipleOfInput(): void
{
$numerics = new Numerics();
$this->expectException(TypeError::class);
$this->expectExceptionMessage('Argument #2 ($multipleOf) must be of type float, string given');
$numerics->mfloor(12.34, 'should fail');
}
}
Loading

0 comments on commit 3aec98f

Please sign in to comment.