Skip to content
Merged

Dev #18

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions benchmarks/ArrayBenchmark.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,33 @@
];
}

public function benchmarkIntArrayFromTrusted(): array
{
$data = range(1, self::ARRAY_SIZE);

$start = microtime(true);
$memoryStart = memory_get_usage();

for ($i = 0; $i < self::ITERATIONS; $i++) {
$array = IntArray::fromTrusted($data);

Check warning on line 173 in benchmarks/ArrayBenchmark.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused "$array" local variable.

See more on https://sonarcloud.io/project/issues?id=Nejcc_php-datatypes&issues=AZ5udYvM6gHRgMLdBDk4&open=AZ5udYvM6gHRgMLdBDk4&pullRequest=18
}

$end = microtime(true);
$memoryEnd = memory_get_usage();

return [
'time' => $end - $start,
'memory' => $memoryEnd - $memoryStart,
'iterations' => self::ITERATIONS,
'type' => 'IntArray::fromTrusted'
];
}

public function runAllBenchmarks(): array
{
return [
'int_array_creation' => $this->benchmarkIntArrayCreation(),
'int_array_from_trusted' => $this->benchmarkIntArrayFromTrusted(),
'native_array_creation' => $this->benchmarkNativeArrayCreation(),
'int_array_operations' => $this->benchmarkIntArrayOperations(),
'native_array_operations' => $this->benchmarkNativeArrayOperations(),
Expand Down
29 changes: 25 additions & 4 deletions benchmarks/IntegerBenchmark.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@

public function benchmarkInt8Arithmetic(): array
{
$int1 = new Int8(50);
$int2 = new Int8(30);
$int1 = new Int8(5);
$int2 = new Int8(3);

$start = microtime(true);
$memoryStart = memory_get_usage();
Expand All @@ -84,8 +84,8 @@

public function benchmarkNativeIntArithmetic(): array
{
$int1 = 50;
$int2 = 30;
$int1 = 5;
$int2 = 3;

$start = microtime(true);
$memoryStart = memory_get_usage();
Expand Down Expand Up @@ -131,10 +131,31 @@
];
}

public function benchmarkInt8Of(): array
{
$start = microtime(true);
$memoryStart = memory_get_usage();

for ($i = 0; $i < self::ITERATIONS; $i++) {
$int = Int8::of(42);

Check warning on line 140 in benchmarks/IntegerBenchmark.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused "$int" local variable.

See more on https://sonarcloud.io/project/issues?id=Nejcc_php-datatypes&issues=AZ5udYu56gHRgMLdBDk3&open=AZ5udYu56gHRgMLdBDk3&pullRequest=18
}

$end = microtime(true);
$memoryEnd = memory_get_usage();

return [
'time' => $end - $start,
'memory' => $memoryEnd - $memoryStart,
'iterations' => self::ITERATIONS,
'type' => 'Int8::of (cached)'
];
}

public function runAllBenchmarks(): array
{
return [
'int8_creation' => $this->benchmarkInt8Creation(),
'int8_of' => $this->benchmarkInt8Of(),
'native_int_creation' => $this->benchmarkNativeIntCreation(),
'int8_arithmetic' => $this->benchmarkInt8Arithmetic(),
'native_int_arithmetic' => $this->benchmarkNativeIntArithmetic(),
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
},
"autoload-dev": {
"psr-4": {
"Nejcc\\PhpDatatypes\\Tests\\": "tests"
"Nejcc\\PhpDatatypes\\Tests\\": "tests",
"Nejcc\\PhpDatatypes\\Benchmarks\\": "benchmarks"
}
},
"scripts": {
Expand Down
101 changes: 7 additions & 94 deletions src/Abstract/AbstractBigInteger.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,15 @@ abstract class AbstractBigInteger implements BigIntegerInterface

/**
* @param int|string $value
* @param bool $trusted Internal use only. When true, skips MIN/MAX validation.
* Used by arithmetic ops that already pre-check the result.
*/
public function __construct(int|string $value)
public function __construct(int|string $value, bool $trusted = false)
{
if ($trusted) {
$this->value = (string)$value;
return;
}
$this->setValue($value);
}

Expand Down Expand Up @@ -77,97 +83,4 @@ protected function setValue(int|string $value): void

$this->value = $valueStr;
}



/**
* @param BigIntegerInterface|NativeIntegerInterface $other
* @param callable $operation
* @param string $operationName
*
* @return $this
*/
protected function performOperation(
BigIntegerInterface|NativeIntegerInterface $other,
callable $operation,
string $operationName
): static {
$result = $operation($this->value, (string)$other->getValue());

if (bccomp($result, (string)static::MIN_VALUE) < 0 || bccomp($result, (string)static::MAX_VALUE) > 0) {
$exceptionClass = bccomp($result, (string)static::MAX_VALUE) > 0 ? \OverflowException::class : \UnderflowException::class;
throw new $exceptionClass('Result is out of bounds.');
}

return new static($result);
}

/**
* @param string $a
* @param string $b
*
* @return string
*/
protected function addValues(string $a, string $b): string
{
return bcadd($a, $b, 0);
}

/**
* @param string $a
* @param string $b
*
* @return string
*/
protected function subtractValues(string $a, string $b): string
{
return bcsub($a, $b, 0);
}

/**
* @param string $a
* @param string $b
*
* @return string
*/
protected function multiplyValues(string $a, string $b): string
{
return bcmul($a, $b, 0);
}

/**
* @param string $a
* @param string $b
*
* @return string
*/
protected function divideValues(string $a, string $b): string
{
if ($b === '0') {
throw new \DivisionByZeroError('Division by zero.');
}

// Check if $a is evenly divisible by $b
$mod = bcmod($a, $b);
if ($mod !== '0') {
throw new \UnexpectedValueException('Division result is not an integer.');
}

return bcdiv($a, $b, 0);
}

/**
* @param string $a
* @param string $b
*
* @return string
*/
protected function modValues(string $a, string $b): string
{
if ($b === '0') {
throw new \DivisionByZeroError('Division by zero.');
}

return bcmod($a, $b);
}
}
68 changes: 63 additions & 5 deletions src/Abstract/AbstractFloat.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@

/**
* @param float $value
* @param bool $trusted Internal use only. When true, skips MIN/MAX and INF validation.
* Used by arithmetic ops that already pre-check the result.
*/
public function __construct(float $value)
public function __construct(float $value, bool $trusted = false)
{
if ($trusted) {
$this->value = $value;
return;
}
$this->setValue($value);
}

Expand All @@ -36,27 +42,79 @@
return $this->value;
}

#[\NoDiscard('add() returns a new immutable Float; the original is unchanged so discarding the result is always a bug')]
final public function add(self $other): static
{
return new static($this->value + $other->value);
$result = $this->value + $other->value;
if (is_infinite($result)) {
throw new OutOfRangeException('INF and -INF are not allowed for this float type.');

Check failure on line 50 in src/Abstract/AbstractFloat.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "INF and -INF are not allowed for this float type." 5 times.

See more on https://sonarcloud.io/project/issues?id=Nejcc_php-datatypes&issues=AZ5udYtp6gHRgMLdBDk0&open=AZ5udYtp6gHRgMLdBDk0&pullRequest=18
}
if ($result > static::MAX_VALUE || $result < static::MIN_VALUE) {
throw new OutOfRangeException(sprintf(
'Value %f is out of range for this float type. Allowed range: [%f, %f]',

Check failure on line 54 in src/Abstract/AbstractFloat.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "Value %f is out of range for this float type. Allowed range: [%f, %f]" 5 times.

See more on https://sonarcloud.io/project/issues?id=Nejcc_php-datatypes&issues=AZ5udYtp6gHRgMLdBDkz&open=AZ5udYtp6gHRgMLdBDkz&pullRequest=18
$result,
static::MIN_VALUE,
static::MAX_VALUE
));
}
return new static($result, true);
}

#[\NoDiscard('subtract() returns a new immutable Float; the original is unchanged so discarding the result is always a bug')]
final public function subtract(self $other): static
{
return new static($this->value - $other->value);
$result = $this->value - $other->value;
if (is_infinite($result)) {
throw new OutOfRangeException('INF and -INF are not allowed for this float type.');
}
if ($result > static::MAX_VALUE || $result < static::MIN_VALUE) {
throw new OutOfRangeException(sprintf(
'Value %f is out of range for this float type. Allowed range: [%f, %f]',
$result,
static::MIN_VALUE,
static::MAX_VALUE
));
}
return new static($result, true);
}

#[\NoDiscard('multiply() returns a new immutable Float; the original is unchanged so discarding the result is always a bug')]
final public function multiply(self $other): static
{
return new static($this->value * $other->value);
$result = $this->value * $other->value;
if (is_infinite($result)) {
throw new OutOfRangeException('INF and -INF are not allowed for this float type.');
}
if ($result > static::MAX_VALUE || $result < static::MIN_VALUE) {
throw new OutOfRangeException(sprintf(
'Value %f is out of range for this float type. Allowed range: [%f, %f]',
$result,
static::MIN_VALUE,
static::MAX_VALUE
));
}
return new static($result, true);
}

#[\NoDiscard('divide() returns a new immutable Float; the original is unchanged so discarding the result is always a bug')]
final public function divide(self $other): static
{
if ($other->value === 0.0) {
throw new \DivisionByZeroError('Division by zero.');
}
return new static($this->value / $other->value);
$result = $this->value / $other->value;
if (is_infinite($result)) {
throw new OutOfRangeException('INF and -INF are not allowed for this float type.');
}
if ($result > static::MAX_VALUE || $result < static::MIN_VALUE) {
throw new OutOfRangeException(sprintf(
'Value %f is out of range for this float type. Allowed range: [%f, %f]',
$result,
static::MIN_VALUE,
static::MAX_VALUE
));
}
return new static($result, true);
}

final public function equals(self $other): bool
Expand Down
Loading
Loading