Coder Social home page Coder Social logo

krowinski / bcmath-extended Goto Github PK

View Code? Open in Web Editor NEW
74.0 6.0 12.0 121 KB

Extends php BCMath lib for missing functions like floor, ceil, round, abs, min, max, rand for big numbers. Also wraps existing BCMath functions.

License: MIT License

PHP 100.00%
bignumber math php arbitrary-precision complex-numbers decimals money ceil floor abs

bcmath-extended's People

Contributors

aywan avatar github-actions[bot] avatar gnutix avatar jimmy4o4 avatar krowinski avatar mbezhanov avatar peter279k avatar sander-toonen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

bcmath-extended's Issues

using hexdec on PHP 7.4 logs: "PHP Deprecated: Invalid characters passed for attempted conversion"

Please provide the following details.

  • Operating System: Linux and OS X
  • PHP Version: 7.4

Steps required to reproduce the problem.

  1. Use hexdec method, for example: BC::hexdec('0x300000000d2c12440c4310c20c2428c20c8330cc0ca318ca0cc330cc0c230806')

Expected Result.

  • Ideally no warnings

Actual Result.

PHP Deprecated: Invalid characters passed for attempted conversion, these have been ignored in /<path>/vendor/krowinski/bcmath-extended/src/BCMathExtended/BC.php on line 438

Currently using version 6.0.0

Make getDecimalsLengthFromNumber() public

Is it possible to make getDecimalsLengthFromNumber() method public instead of private?

In an integration test of a project I'm working on, there's a case where I'd like to do:

$this->assertEquals(BC::round($expectedValue, BC::getDecimalsLengthFromNumber($actualValue), $actualValue);

I need to do this in that test, in order to ensure that a third-party system is returning properly rounded values. The inputs in the test are generated dynamically, and the third-party system outputs a varying number of decimals, depending on the size of the number.

Here are some example inputs and outputs to illustrate this:

--- Expected (inputs generated in the test)
+++ Actual (outputs returned from the third-party system)
@@ @@
-'1399454.22512577'
+'1399454.2251258'
-'959960482.27973165'
+'959960482.27973'

Performance

Please provide the following details.

  • Operating System: Linux
  • PHP Version: 8.1.14

Performance implications

I am using BC::roundDown() on my responses to always round down specific fields to a specific decimal place. Now let's say we have in a for me realistic scenario of 16000 BC::roundDown() operations on my response. It's fine if they are all raw and not already rounded. But what if they are already? In my case the values coming cache and other sources are already rounded to 2 decimals. So why should I run BC::roundDown() again, but at the same time I can not be sure that the source always gives me formatted values?

I did 3 different solutions with 10 million values to be round:

  1. Explode a string and check if number of chars after the dot is <= required decimals
  2. Use preg_match
  3. Just run BC::roundDown as we currently do:

Simple validation with explode takes 2.6 seconds to run.
Using preg_match takes 0.8 seconds to run
Just do what we do now and just run BC::roundDown takes 76 seconds to run

So projected on my current project, I can drop another 10 ms at least from the response generation. And in total reduce CPU time if we can add this simple validation to the library where it makes sense.

Code used for the test

$case = 2;
$testString = '1234.123';
$start = microtime(true);
for ($i = 0; $i < 16000; ++$i) {
    switch ($case) {
        case 0:
            $elements = explode('.', $testString);
            $elementsCount = count($elements);
            if ($elementsCount === 1) {
                continue 2;
            }

            if (strlen(end($elements)) <= 2) {
                // Match
            }

            break;

        case 1:
            if (preg_match("/\d+(\.\d{1,2})?$/", $testString)) {
                // Match
            }

            break;

        case 2:
            BC::roundDown($testString, 2);

            break;
    }
}
dump(microtime(true) - $start);

BC return should always be string

I can see some document saying that the return may be int/float.
But I think BC methods should always return string instead.
do you think we need to type hinting string for the return?

bcmod 7.2 issue

As of 7.2 float can be passed to bcmod but they don't return correct values (IMO)

I created bug for this in https://bugs.php.net/bug.php?id=76287 but it was commented as documentation issue not a bug.

bcmod() doesn't use floor() but rather truncates towards zero,
which is also defined this way for POSIX fmod(), so that the
result always has the same sign as the dividend.  Therefore, this
is not a bug, but rather a documentation issue.

But I still will use floor not truncated for fmod in this lib.

Invalid result for BC::pow with negative fractional exponent

Please provide the following details.

  • Operating System: Linux
  • PHP Version: 7.2

Steps required to reproduce the problem.

For testing I used another library https://php-decimal.io. Following code compares the result of BC::pow and $decimal->pow

public function testNegativeExponentBCVsDecimal()
{
        $base = '1.005';
        $exp = '-120.12345678';
โ€‹
        $v1Dec = new Decimal($base);
        $v1 = $v1Dec->pow(new Decimal($exp))->toString();    
        $v1_old = BC::pow($base, $exp);    
        printf("\n$v1\n$v1_old");
}

Expected Result.

0.5492944034820568190269179210

Actual Result.

0.5499712716356165

So the BC version will start deviating after the third digit after the comma. It does not matter how high you set the scale, it will always be incorrect after a few digits.

NULL handling is different from PHP's BCMath functions

Please provide the following details.

  • Operating System: Linux
  • PHP Version: 7.2, 8.0

Steps required to reproduce the problem.

  1. Call BC::* with a null value

Expected Result.

  • Should work just like the BCMath functions

Actual Result.

  • Fatal error: Uncaught TypeError: BCMathExtended\BC::*(): Argument #* ($*) must be of type string, null given

See this demo with bcadd vs BC::add: https://phpsandbox.io/n/orange-snow-okeg-2feqa
And this Stackoverflow for explanation as to why this happens : https://stackoverflow.com/questions/45600400/php7-methods-with-a-scalar-type-declaration-refuse-to-type-juggle-null-values

PS: obviously, I don't call the lib with an explicit null on purpose ; it's the result of a very long chain of events in a big legacy project...

Request for RoundDown/RoundUp with precision

Here is the functions I am using:

function roundDown($value, $places) : float
{
  $mult = pow(10, abs($places));
  return $places < 0 ?
	floor($value / $mult) * $mult :
	  floor($value * $mult) / $mult;
}

function roundUp($value, $places) : float
{
  $mult = pow(10, abs($places));
  return $places < 0 ?
	ceil($value / $mult) * $mult :
	  ceil($value * $mult) / $mult;
}

But it is not in BC.
I need a BC version and hope it will be added in this project.

Idea: this project should add the existed bc methods

I like this project very much and it adds lots of missing methods from BC.
but the built-in bc is procedural style. and this project is using static OOP.
we still need to use bcadd/bcsub/bcmul/... in procedural style, while for max/min/avg we can use static OOP style. So I would like to suggest adding add/sub/mul/,,, those existing methods in this project. and let this project be more powerful.

Add operator method features

Feature request

As title, I think we can implement these logic operator features are as follows:

  • or, xor and and operators.

Why is the trimming of trailing zeroes enforced by the functions?

I can't understand why would you enforce the trimming of the trailing zeroes after the decimal point?

A global setting, or something simmilar, that can disable this would be nice. Additionally, the class is not extendable at all, as you are calling the static methods with the self:: instead of the static:: caller.

Scientific notation regex

Please provide the following details.

  • Operating System: Ubuntu 16.04.4 LTS
  • PHP Version: 7.2.4
  • php-mysql-replication Version: 5.6.*

Steps required to reproduce the problem.

  1. Have a html <input type="number" /> field
  2. Enter a scientific notation number that does not use + or - in the exponent part (valid format). e.g. 3.456e11.
  3. Send it to a php script that reads this string
  4. Try to use BC::convertScientificNotationToString(...) on it.

Expected Result.

  • "345600000000"

Actual Result.

  • "3.45611"

The problem is that convertScientificNotationToString(int|string|float $number) uses an uncomplete regex to test for a scientific notation. Please check the formal php documentation. It states:

LNUM [0-9]+
DNUM ([0-9]*[\.]{LNUM}) | ({LNUM}[\.][0-9]*)
EXPONENT_DNUM [+-]?(({LNUM} | {DNUM}) [eE][+-]? {LNUM})
  1. The beginning of a number can also have an optional + sign [+-]?
  2. The exponent part has an optional + or - sign [+-]?

Consider dropping support for php5 and php7.0

Consider deprecating/dropping support for php 5.x and 7.0 in the next major version as they officially reached end of life some months ago. See: https://www.php.net/supported-versions.php

Why? Stay up to date & have all the nice things that php7.1+ offers.

Please provide the following details.

  • Operating System: any
  • PHP Version: 5.x|7.0

Steps required to reproduce the problem.

not required

Expected Result.

not required

Actual Result.

not required

Consistent type hints

All public methods should have consistent parameter type hints. I guess it should be int|float|string. Some methods are missing this doc block type hints and because of this, editors and static code analyzers are complaining/marking uses with warnings/errors.

Please also check that all public methods have a correct return type hint.

Please provide the following details.

  • Operating System: any
  • PHP Version: any

Steps required to reproduce the problem.

  1. Use any static code analyzer or editor with intelisense.
  2. Try to pass to different functions different numeric types.

Expected Result.

  • No errors/warnings etc.

Actual Result.

  • Errors/warnings etc. about type miss match.

Scale not applied

A number of methods pass null as scale parameter to bcmath-functions. However, bcmath interprets this as 0. As a user of this library I expect that passing no scale parameter will result in using the scale set by bcscale or the configured scale value in php.ini, not using scale 0.

This problem exists with the following functions:

  • BC::add
  • BC::comp
  • BC::pow
  • BC::powMod
  • BC::sqrt
  • BC::sub

Bug other functions can give wrong results due to this bug too:

  • BC::min
  • BC::max

E.g. the following test fails:

self::assertSame('7.20', BC::min('7.30', '7.20'));

  1. Unit\BCTest::shouldMin
    Failed asserting that two strings are identical.
    --- Expected
    +++ Actual
    @@ @@
    -7.20
    +7.30

BC::pow only supports integer exponents

Please provide the following details.

Would it be possible to implement a bcmath function that can calculate pow with arbitrary precision, using a fractional exponent?

Maybe https://stackoverflow.com/questions/33486170/php-how-to-raise-number-to-tiny-fractional-exponent is a solution?

  • Operating System: Linux
  • PHP Version: 7.2.4

Steps required to reproduce the problem.

  1. BC::pow('5', '0.05', 12)

Expected Result.

Something like this.
1.08379838673

Actual Result.

The bcpow function only supports integer exponents. Try using pow instead.

roundHalfEven working incorrectly?

Please provide the following details.

  • Operating System: Linux
  • PHP Version: 8.0

Steps required to reproduce the problem.

$value = '1.1259';
$scale = 2;
var_dump(round($value, $scale, PHP_ROUND_HALF_EVEN)); // float(1.13)
var_dump(BC::roundHalfEven($value, $scale)); // string(4) "1.12"

Expected Result.

  • Should be '1.13'? Right?

Actual Result.

  • string(4) "1.12"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.