> On Dec 7, 2023, at 00:36, Alex Pravdin <alex.prav...@interi.co> wrote:
> 
> Hello internals,
> 
> 
> This is the second round of the discussion regarding arbitrary precision 
> scalar type integration into PHP. The previous part: 
> https://marc.info/?l=php-internals&m=168250492216838&w=2 was initiated by me 
> before deep diving into the work with decimals in PHP. After 6 months of 
> working, I would like to update my proposal taking into account my experience 
> and the previous discussion.
> 
> Today's alternatives and their problems are the following.
> 
> bcmath:
> - Workaround: using string type.
> - Unintuitive function calls instead of regular math operations.
> - Unintuitive strings instead of numbers. People want to work with numbers.
> - Can not use proper type-hinting.
> - Can use PHP's basic type coercions.
> 
> Ext-decimal:
> - Third-party extension.
> - Workaround: implements the Decimal class that allows basic regular math 
> operations.
> - Requires using class methods for the rest of math operations.
> - The latest release was in 2019 and there's a danger that it will be 
> unmaintained and not compatible with the future PHP releases.
> - The php-decimal documentation website is currently down.
> - Since objects are always casted to true when not null, "(bool) Decimal(0)" 
> will equal to true which is not intuitive.
> - IDEs are often confused when you use math operations on objects while the 
> code works fine.
> 
> GMP:
> - Workaround: implements the GMP class that allows basic math operations.
> - Requires using separate functions for the rest of operations.
> 
> - Objects are always casted to true, GMP(0) will equal to true.
> 
> 
> Accounting for all of the above, I suggest adding a native numeric scalar 
> arbitrary precision type called "decimal". Below are the preliminary 
> requirements for implementation.
> 
> 
> Decimal values can be created from literals by specifying a modifier or using 
> the (decimal) typecast:
> 
> $v = 0.2d;
> $v = (decimal) 0.2; // Creates a decimal value without intermediary float
> 
> It uses the precision and scale defined in php.ini.
> 
> The "decimal" typehint allows to define custom precision and scale: 
> decimal(20,5). It accepts regular expressions returning ints in the execution 
> context. It accepts int constants and literals in class field and function 
> argument definitions.
> 
> New functions added: get_scale and get_precision to return corresponding 
> values about a decimal value.
> 
> If decimal value with different scale and precision is going to be assigned 
> to a variable or parameter with smaller scale or precision, it first tries to 
> convert the value. If it's not possible, then an exception is thrown like 
> "Can not convert decimal (a, b) xxxxx.yyyy to decimal(c, d)". If possible, it 
> performs the conversion and generates a warning like "Assigning decimal(a, b) 
> to decimal(c, d) may be not possible with some values".
> 
> It works the same as "float" in terms of its usage and type casting except 
> for one thing. Float value can be passed to a decimal argument or typecasted 
> with a warning like "Float to decimal conversion may incur unexpected 
> results".
> 
> Decimal to float conversion is allowed and smooth:
> 
> function f (float $value) {}
> 
> f(0.2);
> 
> f(0.2d); // allowed with no warnings
> 
> 
> Function "str_to_decimal" added to convert string representation of numbers 
> to decimals.
> 
> 
> Typecast from string to decimal works the same as the "str_to_decimal" 
> function.
> 
> Function "float_to_decimal" added to explicitly convert floats to decimals. 
> It performs float to string conversions using php.ini settings as defaults 
> but also accepts parameters to configure the conversion. Then, it converts 
> string to decimal. Since the main problem of float to decimal conversion is 
> that we don't know the exact result until we use some rounding when 
> transforming it to a human-readable format, it looks like the step of the 
> conversion to a string is inevitable. Any more optimized algorithms are 
> welcome.
> 
> Explicit typecast from float to decimal works the same as "float_to_decimal" 
> function with all default values but also throws a warning. This is to 
> encourage users to use explicit conversion with the "float_to_decimal" 
> function and control the results.
> 
> Literal numbers in the code are converted to floats by default. If prepended 
> by the "(decimal)" typecast, the decimal result is produced without an 
> intermediary float.
> 
> New declare directive "default_decimal" is added. When used, literals and 
> math operations return decimal by default instead of float. This is to 
> simplify creating source files working with decimals only.
> 
> New language construct "as_decimal()" is added to produce decimal math 
> results for literals and math operations instead of float without 
> intermediary float:
> 
> $var = 5 / 2; // returns float 2.5
> $var = as_decimal(5 / 2); // returns decimal 2.5
> 
> This is a kind of "default_decimal" for a specific operation.
> 
> If mixed float and decimal operands are used in a math operation, decimal is 
> converted to float by default. If "default_decimal" directive or 
> "as_decimal()" construct is used, float is converted to decimal (with a 
> warning):
> 
> $f = (float) 0.2;
> $d = (decimal) 0.2;
> 
> $r = $f + $d; // returns float result by default
> $r = as_decimal($f + $d); // returns decimal result with a warning about 
> implicit float to decimal conversion
> 
> All builtin functions that currently accept float also accept decimal. So 
> users don't need to care about separate function sets, and PHP developers 
> don't need to maintain separate sets of functions. If such functions get the 
> decimal parameter, they return decimal. If they have more than one float 
> parameter and mixed float and decimal passed, decimals converted to float by 
> default. If "default_decimal" or "as_decimal" used, float is converted to 
> decimal with the warning.
> 
> 
> The new type uses libmpdec internally to perform decimal calculations (same 
> as Python).
> 
> 
> All of the points above are subject to discussions, it is not an RFC 
> candidate right now. So please share your opinions.
> 
> I know that the implementation of this will require a lot of work. But I 
> don't think this is a stopper from formulating the requirements. Sometimes, 
> any project requires big changes to move forward. I'm pretty sure this 
> functionality will move PHP to the next level and expand its area of 
> applications. My thoughts here are mostly from the user's perspective, I'm 
> not so familiar with PHP internal implementation. But I think this feature 
> can be a good goal for PHP 9.
> 
> 
> --
> Best regards,
> Alex Pravdin


Someone tangential to your proposal, have you given any thought to implementing 
this similar to how Python treats numbers?

In Python, integers have unlimited precision. I’m not sure how they store the 
values internally, but to users of Python, any sequence of numbers is an `int`, 
no matter how long it is.

For example, this multiplies the max 64-bit, signed integer by 2, and the 
result is still an `int`:

    >>> i = 9223372036854775807 * 2
    >>> i
    18446744073709551614
    >>> type(i)
    <class 'int’>

And this is a really, really, really long number that’s still an `int`:

    >>> x = 
9223372036854775807922337203685477580792233720368547758079223372036854775807
    >>> x
    9223372036854775807922337203685477580792233720368547758079223372036854775807
    >>> type(x)
    <class 'int’>

Python has floating point numbers, but these use the double type in C, so 
they’re not arbitrary in length, like its integers. (Python uses libmpdec, as 
you’ve mentioned, with `decimal.Decimal`.)

However, if we can achieve arbitrary-length integers, is there a reason we 
couldn’t also have arbitrary length floating point numbers?

Below a certain threshold, we could use the system’s native int and double for 
storage and math. Above a certain threshold, we could internally switch to a 
bundled library for handling arbitrary-precision integers and floats, and it 
would be seamless to the user, who would see only `int` and `float` types in 
PHP.

Anyway, this is something I’ve been thinking about for some time, but I haven’t 
taken the time to look into what it might take to implement it. It sounds like 
you have looked into it, so I’m interested to know whether you think what I’ve 
described here is feasible.

Cheers,
Ben





Cheers,
Ben

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php

Reply via email to