V Google Code Jamu jsem už podruhé dojel na zaokrouhlovací chybu. K těm dochází nejen při práci s desetinnými čísly, ale i u velkých celých čísel, s kterými PHP pracuje také jako s desetinnými. V PHP jsou dvě knihovny pro práci s přesnými čísly: BC a GMP, s těmi se ale pracuje mnohem krkolomněji. Porovnejte běžný zápis s využitím knihovny BC:
<?php // běžný zápis $x += $v * $d / 2 - $p; // využití BC $x = bcadd($x, bcsub(bcmul($v, bcdiv($d, '2')), $p)); ?>
Vzpomněl jsem si ale na extenzi Operator, která dovoluje změnit chování operátorů u objektů. S jejím využitím se dá kód alespoň částečně zpátky zjednodušit:
<?php $x = new BcNum($x); $x += new BcNum($v) * $d / 2 - $p; ?>
Objekty implementující přetěžování operátorů by měly být vždy alespoň na levé straně binárního operátoru (s výjimkou operátoru >
, kde je to naopak). Samotná implementace třídy je jednoduchá:
<?php /** Representation of arbitrary precision number with support for common operators * @copyright Jakub Vrána, https://php.vrana.cz/ * @uses PHP extension bc * @uses PECL extension operator */ class BcNum { private $value; /** Create arbitrary precision number * @param string * @uses default scale set by bcscale() */ function __construct($value) { $this->value = (string) $value; } function __toString() { return $this->value; } function __add($value) { return new BcNum(bcadd($this->value, $value)); } function __sub($value) { return new BcNum(bcsub($this->value, $value)); } function __mul($value) { return new BcNum(bcmul($this->value, $value)); } function __div($value) { return new BcNum(bcdiv($this->value, $value)); } function __mod($value) { return new BcNum(bcmod($this->value, $value)); } function __is_identical($value) { return $this->value === $value; } function __is_not_identical($value) { return $this->value !== $value; } function __is_equal($value) { return bccomp($this->value, $value) == 0; } function __is_not_equal($value) { return bccomp($this->value, $value) != 0; } function __is_smaller($value) { return bccomp($this->value, $value) < 0; } function __is_smaller_or_equal($value) { return bccomp($this->value, $value) <= 0; } function __is_larger($value) { return bccomp($this->value, $value) > 0; } function __is_larger_or_equal($value) { return bccomp($this->value, $value) >= 0; } function __assign_add($value) { return new BcNum($this->value = bcadd($this->value, $value)); } function __assign_sub($value) { return new BcNum($this->value = bcsub($this->value, $value)); } function __assign_mul($value) { return new BcNum($this->value = bcmul($this->value, $value)); } function __assign_div($value) { return new BcNum($this->value = bcdiv($this->value, $value)); } function __assign_mod($value) { return new BcNum($this->value = bcmod($this->value, $value)); } function __pre_inc() { return $this->value = bcadd($this->value, '1'); } function __pre_dec() { return $this->value = bcsub($this->value, '1'); } function __post_inc() { $return = $this->value; $this->value = bcadd($this->value, '1'); return $return; } function __post_dec() { $return = $this->value; $this->value = bcsub($this->value, '1'); return $return; } } ?>
Chybí jen podpora pro unární mínus.
Diskuse je zrušena z důvodu spamu.