Skip to content
Merged
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
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

All notable changes to this project will be documented in this file.

## UNRELEASED (0.20.0)

💥 **Breaking changes**

The following breaking change only affects you if you specifically catch `DivisionByZeroException` around calls to `of()`:

- `of()` now throws `NumberFormatException` instead of `DivisionByZeroException` when the string is a fraction with a denominator of zero, such as `'2/0'`

✨ **New features**

- New methods: `parse()` and `parseNullable()` safely parse untrusted input, by restricting the allowed syntax and limiting the number of digits
- New enum: `NumberSyntax` lists the syntax features that `parse()` can accept, with constants for the most common combinations

🐛 **Bug fixes**

- `of()` no longer throws `PlatformException` on malformed input with many digits, crafted to trigger heavy backtracking in its parser; such input now throws `NumberFormatException` as documented
- `of()` now throws `NumberFormatException` for exponents at the edge of the integer range, previously exhausted memory
- `NumberFormatException` messages now escape control and non-ASCII characters, and truncate values longer than 40 bytes, instead of copying the raw input into the message

## [0.19.1](https://github.com/brick/math/releases/tag/0.19.1) - 2026-08-08

✨ **New features**
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@ BigRational::of('1.15'); // 23/20 (reduced to lowest terms)
> BigDecimal::fromFloatShortest(0.1); // 0.1
> ```

> [!CAUTION]
> The `of()` factory method is for trusted input: a string as short as `1e1000000000` can expand to gigabytes of
> memory and exceed PHP's memory limit.
>
> For untrusted user input, use `parse()` instead:
>
> ```php
> BigDecimal::parse('1000000000000000000000', allowedSyntax: NumberSyntax::SCIENTIFIC, maxDigits: 100); // OK
> BigDecimal::parse('1e1000000000', allowedSyntax: NumberSyntax::SCIENTIFIC, maxDigits: 100); // NumberFormatException
> ```

#### Immutability & chaining

The `BigInteger`, `BigDecimal` and `BigRational` classes are immutable: their value never changes,
Expand Down Expand Up @@ -148,6 +159,10 @@ echo BigInteger::of(2)->multipliedBy(BigDecimal::of('2.5')); // RoundingNecessar
echo BigDecimal::of(2.5)->multipliedBy(BigInteger::of(2)); // 5.0
```

> [!CAUTION]
> These parameters are converted with `of()`, so the same caution applies: never pass an untrusted string
> directly to an arithmetic or comparison method — `parse()` it first, and pass the resulting number.

#### Division & rounding

##### BigInteger
Expand Down
7 changes: 7 additions & 0 deletions src/BigDecimal.php
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,13 @@ protected static function from(BigNumber $number): static
return $number->toBigDecimal();
}

#[Override]
protected function digitCount(): int
{
// At least one integral digit is written: 0.001 counts 4 digits.
return max($this->getPrecision(), $this->scale + 1);
}

/**
* Puts the internal values of the given decimal numbers on the same scale.
*
Expand Down
6 changes: 6 additions & 0 deletions src/BigInteger.php
Original file line number Diff line number Diff line change
Expand Up @@ -1440,6 +1440,12 @@ protected static function from(BigNumber $number): static
return $number->toBigInteger();
}

#[Override]
protected function digitCount(): int
{
return strlen($this->value) - (int) ($this->value[0] === '-');
}

/**
* Returns random bytes from the provided generator or from random_bytes().
*
Expand Down
Loading
Loading