Skip to content

Commit

Permalink
breakchange: refactor asRound method
Browse files Browse the repository at this point in the history
  • Loading branch information
d8vjork committed Jan 4, 2024
1 parent 5c4584a commit 6af3457
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 40 deletions.
66 changes: 44 additions & 22 deletions src/ByteUnitConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ final class ByteUnitConverter

private DataUnit $dataUnit = DataUnit::B;

private int $precision = 2;
private int $precision = 4;

private bool $outputAsRoundNumber = false;
private int|bool $roundAsMuchAs = 2;

private bool $unitLabelAsOutput = false;

Expand All @@ -45,7 +45,7 @@ public static function new(int|string $bytes): static
*/
public static function from(int|float|string $value, ByteUnit $unit, int $precision = 2): static

Check warning on line 46 in src/ByteUnitConverter.php

View workflow job for this annotation

GitHub Actions / PHP8.3 - ubuntu-latest

Escaped Mutant for Mutator "DecrementInteger": --- Original +++ New @@ @@ /** * Create new instance converting from value and unit to bytes for conversions. */ - public static function from(int|float|string $value, ByteUnit $unit, int $precision = 2) : static + public static function from(int|float|string $value, ByteUnit $unit, int $precision = 1) : static { return new self(static::toBytesFromUnit((string) $value, $unit, $precision)); }

Check warning on line 46 in src/ByteUnitConverter.php

View workflow job for this annotation

GitHub Actions / PHP8.3 - ubuntu-latest

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ /** * Create new instance converting from value and unit to bytes for conversions. */ - public static function from(int|float|string $value, ByteUnit $unit, int $precision = 2) : static + public static function from(int|float|string $value, ByteUnit $unit, int $precision = 3) : static { return new self(static::toBytesFromUnit((string) $value, $unit, $precision)); }
{
return new static(static::toBytesFromUnit((string) $value, $unit, $precision));
return new self(static::toBytesFromUnit((string) $value, $unit, $precision));
}

/**
Expand All @@ -60,41 +60,61 @@ public static function toBytesFromUnit(string $value, ByteUnit $unit, int $preci
return $value;
}

return bcmul(self::numberFormat($value), $unit->asNumber(), $precision);
return bcmul($value, $unit->asNumber(), $precision);

Check warning on line 63 in src/ByteUnitConverter.php

View workflow job for this annotation

GitHub Actions / PHP8.3 - ubuntu-latest

Escaped Mutant for Mutator "BCMath": --- Original +++ New @@ @@ if ($unit->value === '1') { return $value; } - return bcmul($value, $unit->asNumber(), $precision); + return (string) ($value * $unit->asNumber()); } /** * Like PHP's number_format function but without thousands separator.
}

/**
* Like PHP's number_format function but without thousands separator.
*/
public static function numberFormat(string $num, int $decimals = 0): string
public static function numberFormat(string $number, int $decimals = 0): string

Check warning on line 69 in src/ByteUnitConverter.php

View workflow job for this annotation

GitHub Actions / PHP8.3 - ubuntu-latest

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ /** * Like PHP's number_format function but without thousands separator. */ - public static function numberFormat(string $number, int $decimals = 0) : string + public static function numberFormat(string $number, int $decimals = 1) : string { $formattedNumber = number_format(num: (float) $number, decimals: $decimals, thousands_separator: ''); if (!str_contains($formattedNumber, '.')) {
{
return number_format(
num: (float) $num,
$formattedNumber = number_format(
num: (float) $number,
decimals: $decimals,
thousands_separator: ''
);

if (! str_contains($formattedNumber, '.')) {
return $formattedNumber;
}

return rtrim(rtrim($formattedNumber, '0'), '.');
}

/**
* Format numeric string using round by calculated user-input decimals if specified.
*/
private function roundNumericString(string $number): string
{
if ($this->unit === ByteUnit::B || $this->outputAsRoundNumber) {
$decimalPositions = $number[0] === '0' ? 1 : 0;
} else {
$decimalPositions = (int) strpos(strrev($number), '.');
[$whole, $decimal] = explode('.', $number) + ['0', ''];

$numberDecimalPositions = strlen($decimal);

if (! $this->roundAsMuchAs) {
return static::numberFormat($number, $numberDecimalPositions);
}

$decimalPositions = $this->roundAsMuchAs === true
? $numberDecimalPositions
: $this->roundAsMuchAs;

if ($numberDecimalPositions <= $decimalPositions) {
return static::numberFormat($number, strspn($decimal, '0') ?: 1);
}

$result = self::numberFormat($number, $decimalPositions);
$firstRound = static::numberFormat($number, $numberDecimalPositions);

[$firstRoundWhole, $firstRoundDecimal] = explode('.', $firstRound) + ['0', ''];

if (! $this->outputAsRoundNumber) {
return $result;
$numberDecimalPositions = strlen($firstRoundDecimal);

if ($numberDecimalPositions <= $decimalPositions) {
return $firstRound;
}

// We cannot guess without performing a number formatting first
// and take peek at the result to see if can still be rounded.
return str_replace(str_pad('.', ($decimalPositions ?: 1) + 1, '0'), '', $result);
$secondRound = static::numberFormat($firstRound, --$numberDecimalPositions);

return $secondRound > 0 ? $secondRound : $firstRound;
}

/**
Expand All @@ -108,11 +128,13 @@ public function setPrecision(int $precision): self
}

/**
* Round resulting bytes number to have no decimal digits.
* Round resulting bytes number to have as near as the specified decimal digits.
*
* For e.g. asRound(true) = 1, asRound(false) = -1 (all decimals)
*/
public function asRound(bool $value = true): self
public function asRound(int|bool $value = true): self
{
$this->outputAsRoundNumber = $value;
$this->roundAsMuchAs = $value;

return $this;
}
Expand All @@ -132,7 +154,7 @@ public function useUnitLabel(bool $value = true): self
/**
* Assign nearest bytes unit by metric system from current bytes value.
*/
public function nearestUnit(?MetricSystem $metric = MetricSystem::Decimal, ByteUnit $stoppingAt = null): self
public function nearestUnit(?MetricSystem $metric = MetricSystem::Decimal, ?ByteUnit $stoppingAt = null): self
{
$nearestUnit = null;
$byteUnits = ByteUnit::cases();
Expand Down Expand Up @@ -193,7 +215,7 @@ public function sub(int|float|string $quantity, ByteUnit $unit): self
*/
public function getValue(): string
{
$value = bcmul($this->dataUnit->value, self::numberFormat($this->bytes), 0);
$value = bcmul($this->dataUnit->value, $this->bytes, $this->precision);

$value = bcdiv($value, $this->unit->asNumber(), $this->precision);

Expand Down
36 changes: 18 additions & 18 deletions tests/ByteUnitConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ public function testConversionBetweenBinaryByteUnits()
{
$this->assertEquals('1024', ByteUnitConverter::from('1', ByteUnit::MiB)->to(ByteUnit::KiB)->asRound()->getValue());
$this->assertEquals('1', ByteUnitConverter::from('1024', ByteUnit::KiB)->to(ByteUnit::MiB)->asRound()->getValue());
$this->assertEquals('0.0009765625', ByteUnitConverter::from('1', ByteUnit::KiB)->to(ByteUnit::MiB)->setPrecision(10)->getValue());
$this->assertEquals('0.000976563', ByteUnitConverter::from('1', ByteUnit::KiB)->to(ByteUnit::MiB)->setPrecision(10)->getValue());

$bytes = '1099511627776';

$this->assertEquals('0.0000000000000000008673617381445643', ByteUnitConverter::new($bytes)->setPrecision(34)->toQiB()->getValue());
$this->assertEquals('0.0000000000000008881784191874108', ByteUnitConverter::new($bytes)->setPrecision(31)->toRiB()->getValue());
$this->assertEquals('0.0000000000009094947014830074', ByteUnitConverter::new($bytes)->setPrecision(28)->toYiB()->getValue());
$this->assertEquals('0.0000000009313225751814163', ByteUnitConverter::new($bytes)->setPrecision(25)->toZiB()->getValue());
$this->assertEquals('0.00000095367432021694', ByteUnitConverter::new($bytes)->setPrecision(20)->toEiB()->getValue());
$this->assertEquals('0.0009765624', ByteUnitConverter::new($bytes)->setPrecision(10)->toPiB()->getValue());
$this->assertEquals('0.0000000000000000008673617381445643', ByteUnitConverter::new($bytes)->setPrecision(34)->asRound(false)->toQiB()->getValue());
$this->assertEquals('0.0000000000000008881784191874108', ByteUnitConverter::new($bytes)->setPrecision(31)->asRound(false)->toRiB()->getValue());
$this->assertEquals('0.0000000000009094947014830074', ByteUnitConverter::new($bytes)->setPrecision(28)->asRound(false)->toYiB()->getValue());
$this->assertEquals('0.0000000009313225751814163', ByteUnitConverter::new($bytes)->setPrecision(25)->asRound(false)->toZiB()->getValue());
$this->assertEquals('0.00000095367432021694', ByteUnitConverter::new($bytes)->setPrecision(20)->asRound(false)->toEiB()->getValue());
$this->assertEquals('0.0009765624', ByteUnitConverter::new($bytes)->setPrecision(10)->asRound(false)->toPiB()->getValue());
$this->assertEquals('1', ByteUnitConverter::new($bytes)->toTiB()->asRound()->getValue());
$this->assertEquals('1024', ByteUnitConverter::new($bytes)->toGiB()->asRound()->getValue());
$this->assertEquals('1048576', ByteUnitConverter::new($bytes)->toMiB()->asRound()->getValue());
Expand All @@ -56,12 +56,12 @@ public function testConversionBetweenDecimalByteUnits()

$bytes = '1000000000000';

$this->assertEquals('0.0000000000000000009', ByteUnitConverter::new($bytes)->toQB()->setPrecision(19)->getValue());
$this->assertEquals('0.0000000000000009', ByteUnitConverter::new($bytes)->toRB()->setPrecision(16)->getValue());
$this->assertEquals('0.0000000000010', ByteUnitConverter::new($bytes)->toYB()->setPrecision(13)->getValue());
$this->assertEquals('0.0000000010', ByteUnitConverter::new($bytes)->toZB()->setPrecision(10)->getValue());
$this->assertEquals('0.0000010', ByteUnitConverter::new($bytes)->toEB()->setPrecision(7)->getValue());
$this->assertEquals('0.001', ByteUnitConverter::new($bytes)->toPB()->setPrecision(3)->getValue());
$this->assertEquals('0.0000000000000000009', ByteUnitConverter::new($bytes)->toQB()->setPrecision(19)->asRound(false)->getValue());
$this->assertEquals('0.0000000000000009', ByteUnitConverter::new($bytes)->toRB()->setPrecision(16)->asRound(false)->getValue());
$this->assertEquals('0.000000000001', ByteUnitConverter::new($bytes)->toYB()->setPrecision(13)->asRound(false)->getValue());
$this->assertEquals('0.000000001', ByteUnitConverter::new($bytes)->toZB()->setPrecision(10)->asRound(false)->getValue());
$this->assertEquals('0.000001', ByteUnitConverter::new($bytes)->toEB()->setPrecision(7)->asRound(false)->getValue());
$this->assertEquals('0.001', ByteUnitConverter::new($bytes)->toPB()->setPrecision(3)->asRound(false)->getValue());
$this->assertEquals('1', ByteUnitConverter::new($bytes)->toTB()->asRound()->getValue());
$this->assertEquals('1000', ByteUnitConverter::new($bytes)->toGB()->asRound()->getValue());
$this->assertEquals('1000000', ByteUnitConverter::new($bytes)->toMB()->asRound()->getValue());
Expand All @@ -70,9 +70,9 @@ public function testConversionBetweenDecimalByteUnits()

public function testConversionBetweenDifferentMetricSystems()
{
$this->assertEquals('1048.57', ByteUnitConverter::from('1', ByteUnit::MiB)->to(ByteUnit::KB)->getValue());
$this->assertEquals('1048.58', ByteUnitConverter::from('1', ByteUnit::MiB)->to(ByteUnit::KB)->getValue());
$this->assertEquals('1.02', ByteUnitConverter::from('1000', ByteUnit::KiB)->to(ByteUnit::MB)->getValue());
$this->assertEquals('1.04', ByteUnitConverter::from('1024', ByteUnit::KiB)->to(ByteUnit::MB)->getValue());
$this->assertEquals('1.049', ByteUnitConverter::from('1024', ByteUnit::KiB)->to(ByteUnit::MB)->getValue());
}

public function testToBitsFromOneByteEquals8()
Expand Down Expand Up @@ -167,7 +167,7 @@ public function testConversionToBytesDoesNotGiveDecimals()
public function testConversionToNearestUnitPrintsConvertedByteUnitAndMetricSystem()
{
$this->assertEquals(
'1.00 KB',
'1 KB',
(string) ByteUnitConverter::new('1000')->nearestUnit()
);

Expand All @@ -177,7 +177,7 @@ public function testConversionToNearestUnitPrintsConvertedByteUnitAndMetricSyste
);

$this->assertEquals(
'1.00 KiB',
'1 KiB',
(string) ByteUnitConverter::new('1024')->nearestUnit(MetricSystem::Binary)
);

Expand All @@ -187,7 +187,7 @@ public function testConversionToNearestUnitPrintsConvertedByteUnitAndMetricSyste
);

$this->assertEquals(
'0.97 KiB',
'0.977 KiB',
(string) ByteUnitConverter::new('1000')->nearestUnit(MetricSystem::Binary, ByteUnit::KiB)
);
}
Expand Down

0 comments on commit 6af3457

Please sign in to comment.