# Proposal: Arbitrary precision native scalar type

- Thread: https://externals.io/message/121938
- Original archive: https://news-web.php.net/php.internals/121938
- Started by Alex Pravdin on 2023-12-07T06:36:14+00:00
- Messages: 52
- Page 1 of 3: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/121938.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #121938 — Alex Pravdin — 2023-12-07T06:36:14+00:00

URL: https://externals.io/message/121938#121938

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

---

## #121940 — Ben Ramsey — 2023-12-07T07:37:01+00:00

URL: https://externals.io/message/121938#121940 · In reply to: #121938

> 
> 
> 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`:

> > […]
    18446744073709551614
> > […]
    <class 'int’>

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

> > […]
    9223372036854775807922337203685477580792233720368547758079223372036854775807
> > […]
    <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

---

## #121943 — Alexander Pravdin — 2023-12-07T11:26:30+00:00

URL: https://externals.io/message/121938#121943 · In reply to: #121940

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

Unfortunately, I don't know many details of numbers treatment in Python...


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`:
>
> > […]
>     18446744073709551614
> > […]
>     <class 'int’>
>
> And this is a really, really, really long number that’s still an `int`:
>
> > […]
> 9223372036854775807922337203685477580792233720368547758079223372036854775807
> > […]
>
> 9223372036854775807922337203685477580792233720368547758079223372036854775807
> > […]
>     <class 'int’>
>

I'm not sure about integers, I mostly care about decimals :) Decimals can
hold big rounded ints as well. The limit on the maximum number here will be
the underlying library's limit.


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

I'm not familiar with arbitrary-length integers so I don't know what to say
here...



> 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.
>

I guess this could be a topic of the next step discussion after "decimal"
introduction.


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.
>

I was only thinking about decimals and I'm not an expert in low-level
manipulation of numbers.



Best regards,
Alex.

---

## #121944 — Jordan LeDoux — 2023-12-07T14:27:37+00:00

URL: https://externals.io/message/121938#121944 · In reply to: #121938

> 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.
> 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.
>
>
You are going to run into some very difficult corners on this one. For the
last... 8 years i guess? I have been working on an arbitrary precision
library for PHP in userland. It utilizes BCMath, ext-decimal, and GMP,
depending on what is installed and what kind of operation you are
performing.

So, the functions that currently accept floats include functions such as
`sin()` or `atan()`. These functions are not at all trivial to do to
arbitrary precision. In fact, the VAST majority of the work I have done on
my library has been to handle cases like "What do I do if the user wants
2.921 ** 4.293810472934854?" or "How will I guarantee the precision that
the user requested for `tan()` in an efficient way?"

Now, libmpdec is going to actually have implementations for most of this
stuff that you can call directly, but if the PHP function `sin()` isn't
capable of *outputting* a `decimal`, then this isn't really that much of an
improvement over the current situation. I mean, it's still something I
would support, but that's the area where some help from the engine goes a
VERY long way.

You're probably also going to need a way for a user to grab certain
constants to a given precision. I would think e and pi at the least.

I have a lot of experience working on this problem space in PHP, and am
definitely willing to help. I looked into doing a libmpdec PHP integration
several years ago, but decided that voters were unlikely to give it a fair
shot, and so decided against it. If you're willing to try and roll the ball
up the hill though, I'll definitely help you.

Jordan

---

## #121945 — G. P. B. — 2023-12-07T14:36:31+00:00

URL: https://externals.io/message/121938#121945 · In reply to: #121938

> Hello internals,
> [...]
> 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.
>

This is incorrect, GMP object do _not_ support casts to bool
See https://3v4l.org/LHpD1


> 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
>

I disagree with this part, floats can not represent decimal values properly
e.g.
$f1 = 0.1;
$f2 = 0.2;
$f3 = 0.3;
var_dump($f1 + $f2 === $f3);

will return false.
As such, floats are not at all compatible with decimals.

Moreover, the whole point of adding a warning when implicit conversions
from int to float happen was to be able to warn before elevating the
warning to a TypeError.
Therefore, introducing behaviour that warns instead of throwing a TypeError
is already a no-go from my PoV.


> [...]
>
> 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.
>

This behaviour is rather suboptimal, I'd rather have literals be decimals,
as decimals to floats should always be a reasonable implicit coercion
considering we already do int to 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.
>

Please no, no new declare directives that affect engine behaviour.
Strict types was already a mistake because of this IMHO.


> 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.
>

Again, this should return a decimal instead IMHO.


> 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.
>

Messing with the return value type has already proved controversial.
And as I said previously, I am dead against having implicit float to
decimal conversions.
Making the functions type generic is something that I am fine, however.


> The new type uses libmpdec internally to perform decimal calculations
> (same
> as Python).
>

I really don't think we need arbitrary precision decimals.
I'm also not convinced using a floating point spec is the most sensible,
due to the rounding errors this is going to introduce.
The non-arbitrary IEEE 754-2008 specification cannot describe the decimal
123457.1467 exactly, which is frankly pointless.

Decimals, or more broadly rational numbers that, outside numerical
computations, that are going to be used are not going to need huge
denominators.

I've been thinking about this for a while, and I personally think that
rational numbers are just better than trying to support only decimals.
And considering we have 64 bits to play with for a new zval type splitting
the bits into a 32-bit unsigned integer for the numerator and an 32-bit
signed integer for the denominator should provide us with enough reasonable
rational numbers.
As any number that do not fit in this structure seems to be something
relegated to the world of numerical computation where floats are what are
mostly used anyway.


> 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.
>

Yes, this is a major project, and as said above I have also thought about
adding a rational number type.
But the main hurdle for this is changing the zval structure and applying
the change everywhere to the engine and extensions.

Best regards,

Gina P. Banyard

---

## #121960 — Alexander Pravdin — 2023-12-08T09:14:10+00:00

URL: https://externals.io/message/121938#121960 · In reply to: #121944

On Thu, Dec 7, 2023 at 11:27 PM Jordan LeDoux <jordan.ledoux@gmail.com>
wrote:

You are going to run into some very difficult corners on this one. For the
> last... 8 years i guess? I have been working on an arbitrary precision
> library for PHP in userland. It utilizes BCMath, ext-decimal, and GMP,
> depending on what is installed and what kind of operation you are
> performing.
>
> So, the functions that currently accept floats include functions such as
> `sin()` or `atan()`. These functions are not at all trivial to do to
> arbitrary precision. In fact, the VAST majority of the work I have done on
> my library has been to handle cases like "What do I do if the user wants
> 2.921 ** 4.293810472934854?" or "How will I guarantee the precision that
> the user requested for `tan()` in an efficient way?"
>
> Now, libmpdec is going to actually have implementations for most of this
> stuff that you can call directly, but if the PHP function `sin()` isn't
> capable of *outputting* a `decimal`, then this isn't really that much of an
> improvement over the current situation. I mean, it's still something I
> would support, but that's the area where some help from the engine goes a
> VERY long way.
>

In my proposal, builtin functions are capable of returning decimals if
they're fed with decimals.


You're probably also going to need a way for a user to grab certain
> constants to a given precision. I would think e and pi at the least.
>
> I have a lot of experience working on this problem space in PHP, and am
> definitely willing to help. I looked into doing a libmpdec PHP integration
> several years ago, but decided that voters were unlikely to give it a fair
> shot, and so decided against it. If you're willing to try and roll the ball
> up the hill though, I'll definitely help you.
>

Unfortunately, I'm not an expert in precise math. I definitely need help
from experts like you. I appreciate your readiness.

I understand that there are a lot of edge cases. I would propose to
implement basics at first and then decide what to do with edge cases.

---

## #121961 — Alexander Pravdin — 2023-12-08T10:14:44+00:00

URL: https://externals.io/message/121938#121961 · In reply to: #121945

- Objects are always casted to true, GMP(0) will equal to true.
> > […]
>
> This is incorrect, GMP object do _not_ support casts to bool
> See https://3v4l.org/LHpD1
>

This is weird. Any PHP user would expect that a zero number can be easily
casted to boolean false. This is also why I think we need a scalar decimal
type.



>
> > […]
>
> I disagree with this part, floats can not represent decimal values
> properly e.g.
> $f1 = 0.1;
> $f2 = 0.2;
> $f3 = 0.3;
> var_dump($f1 + $f2 === $f3);
>
will return false.
> As such, floats are not at all compatible with decimals.
>

Yes, I know that. I mentioned that we can not convert float to decimal
without an intermediate value where we apply rounding and get a
human-readable value in some representation (string or whatsoever).

My intention was to provide implicit conversion with warnings because I
guess that the majority of end users will expect that numeric scalar types
are interoperable. To not make them scared and point them to issues in
their code instead of slapping their hands immediately when they run
something that is not so correct.



> Moreover, the whole point of adding a warning when implicit conversions
> from int to float happen was to be able to warn before elevating the
> warning to a TypeError.
>

Sorry, I'm not aware of issues of converting ints to floats. Are there any
issues? I understand the issue of the backward conversion, but not the
forward one. Could you add details here?



> Therefore, introducing behaviour that warns instead of throwing a
> TypeError is already a no-go from my PoV.
>

I'm okay with TypeErrors for float to decimal conversions as well. This is
not a key point of my proposal. If the community prefers errors instead of
warnings - that's also fine.


Literal numbers in the code are converted to floats by default. If
> > […]
>
> This behaviour is rather suboptimal, I'd rather have literals be decimals,
> as decimals to floats should always be a reasonable implicit coercion
> considering we already do int to float.
>

Is it optimal to perform conversions on every fractional literal? This is
also not a key point for me and I'm okay with any implementation that the
internals community prefers.



>  New declare directive "default_decimal" is added. When used, literals and
>
> > […]
>
> Please no, no new declare directives that affect engine behaviour.
> Strict types was already a mistake because of this IMHO.
>

I didn't know that the strict types directive was a mistake. My intention
is to be able to write clean all-decimal units of code and not break the
backward compatibility. The old code should work as it was before. At the
same time, there are use cases when the whole class/project should be
written with decimals only. As a user, I want to do that without complex
language structures and excessive typehints or explicit conversions. The
all-decimal code  should be as clean as it is now with floats. This is why
I proposed this directive. Can you suggest something better to achieve the
same?



> New language construct "as_decimal()" is added to produce decimal math
> > […]
>
> Again, this should return a decimal instead IMHO.
>

While it will work from the logical perspective, additional conversions
from decimal to float may incur performance issues in the existing code
that relies on floats only. Again, my intention here is to not break
backward compatibility and not introduce performance issues to the existing
code.



>
>
> > […]
>
> Messing with the return value type has already proved controversial.
> And as I said previously, I am dead against having implicit float to
> decimal conversions.
> Making the functions type generic is something that I am fine, however.
>

I'm fine with generic functions as well. I'm most probably not aware of all
the ongoing discussions. If the internals community prefers generic
functions, I'm absolutely fine with it.




>
>
> > […]
>
> I really don't think we need arbitrary precision decimals.
>
I'm also not convinced using a floating point spec is the most sensible,
> due to the rounding errors this is going to introduce.
> The non-arbitrary IEEE 754-2008 specification cannot describe the decimal
> 123457.1467 exactly, which is frankly pointless.
>
> Decimals, or more broadly rational numbers that, outside numerical
> computations, that are going to be used are not going to need huge
> denominators.
>
> I've been thinking about this for a while, and I personally think that
> rational numbers are just better than trying to support only decimals.
> And considering we have 64 bits to play with for a new zval type splitting
> the bits into a 32-bit unsigned integer for the numerator and an 32-bit
> signed integer for the denominator should provide us with enough reasonable
> rational numbers.
> As any number that do not fit in this structure seems to be something
> relegated to the world of numerical computation where floats are what are
> mostly used anyway.
>

If you can suggest a better way of working with fractional numbers - please
do :) But IMO limiting the language by 64 bits is not a good way. It is
more easy to go over the limit than you may think :) Especially in some
intermediary values while performing complex calculations. I could be
wrong, but fractional numbers limited to 64 bits sound like a bandaid. The
language will tell users "hey, you can do something general, but if you
want something else please don't use me at all or involve bandaids with
extensions". My key point is not only to allow working with decimals, but
also to make all the alternatives useless, cover their functionality by
builtin tools, to free users from thinking about workarounds and bandaids.
From my POV, introducing a new decimal type that does not cover the
GMP/bcmath/ext-decimal functionality is pointless and a waste of time.



>
>
> > […]
>
> Yes, this is a major project, and as said above I have also thought about
> adding a rational number type.
> But the main hurdle for this is changing the zval structure and applying
> the change everywhere to the engine and extensions.
>

I understand that changing something that is at the core of the whole
project is a huge butthurt. But let's focus on end benefits :) I love PHP
but it is also well-known that it is perceived as a language NOT for
"serious and mature" projects. Partly because if you try working with
financial data for a bank app, for example, you immediately have troubles
choosing a workaround to work with fractional numbers and the code you
write will look weird. How quickly you would be able to read something like
this: gmp_add(gmp_mul(gmp_div(gmp_sub($value, $add), $value2, $factor,
gmp_sin($value3), gmp_intval($value))) ? This absence of intuitive
readability makes the development and support difficult and it's better to
choose another language. This is what I want to change and make PHP
powerful enough for this kind of applications.


Best regards,
Alex.

---

## #121988 — Alexander Pravdin — 2023-12-12T08:50:56+00:00

URL: https://externals.io/message/121938#121988 · In reply to: #121938

Ext-decimal:
> ...
> - Workaround: implements the Decimal class that allows basic regular math
> operations.
>

I just found out that under PHP 8.3, basic arithmetic operations on the
Decimal object variables are no longer supported and cause TypeError. So
this is one more point to implementing native decimals in PHP.

Best,
Alex

---

## #121989 — G. P. B. — 2023-12-12T13:03:50+00:00

URL: https://externals.io/message/121938#121989 · In reply to: #121961

On Fri, 8 Dec 2023 at 10:14, Alexander Pravdin <alex.pravdin@interi.co>
wrote:

> 
>
> - Objects are always casted to true, GMP(0) will equal to true.
> > […]
>
> This is weird. Any PHP user would expect that a zero number can be easily
> casted to boolean false. This is also why I think we need a scalar decimal
> type.
>

Internal objects can overload casts, SimpleXML overloads the boolean cast
already.
We could add this to GMP to return false for 0.


> It works the same as "float" in terms of its usage and type casting except
> > […]
> will return false.
> > […]
>
> Yes, I know that. I mentioned that we can not convert float to decimal
> without an intermediate value where we apply rounding and get a
> human-readable value in some representation (string or whatsoever).
>
> My intention was to provide implicit conversion with warnings because I
> guess that the majority of end users will expect that numeric scalar types
> are interoperable. To not make them scared and point them to issues in
> their code instead of slapping their hands immediately when they run
> something that is not so correct.
>
>
>
> > […]
>
> Sorry, I'm not aware of issues of converting ints to floats. Are there any
> issues? I understand the issue of the backward conversion, but not the
> forward one. Could you add details here?
>

I meant converting floats to int.
But you can lose precision when converting an integer to float as there are
only 53 bits for the coefficient.


> Therefore, introducing behaviour that warns instead of throwing a
> > […]
>
> I'm okay with TypeErrors for float to decimal conversions as well. This is
> not a key point of my proposal. If the community prefers errors instead of
> warnings - that's also fine.
>
>
> Literal numbers in the code are converted to floats by default. If
> > […]
>
> Is it optimal to perform conversions on every fractional literal? This is
> also not a key point for me and I'm okay with any implementation that the
> internals community prefers.
>
>
>
> > […]
>
> I didn't know that the strict types directive was a mistake. My intention
> is to be able to write clean all-decimal units of code and not break the
> backward compatibility. The old code should work as it was before. At the
> same time, there are use cases when the whole class/project should be
> written with decimals only. As a user, I want to do that without complex
> language structures and excessive typehints or explicit conversions. The
> all-decimal code  should be as clean as it is now with floats. This is why
> I proposed this directive. Can you suggest something better to achieve the
> same?
>

The issue is that I don't think having arbitrary precision decimals as a
core language feature is a necessity compared to rational types.
A cast from rational to float wouldn't produce a large round trip, whereas
trying to figure out arbitrary precision is more difficult.
But in any case, having a declare/INI or whatever that changes the
behaviour of the engine/language is not a good design choice.


> [...]
>
>  The new type uses libmpdec internally to perform decimal calculations
> > […]
> I'm also not convinced using a floating point spec is the most sensible,
> > […]
>
> If you can suggest a better way of working with fractional numbers -
> please do :) But IMO limiting the language by 64 bits is not a good way. It
> is more easy to go over the limit than you may think :) Especially in some
> intermediary values while performing complex calculations. I could be
> wrong, but fractional numbers limited to 64 bits sound like a bandaid. The
> language will tell users "hey, you can do something general, but if you
> want something else please don't use me at all or involve bandaids with
> extensions". My key point is not only to allow working with decimals, but
> also to make all the alternatives useless, cover their functionality by
> builtin tools, to free users from thinking about workarounds and bandaids.
> From my POV, introducing a new decimal type that does not cover the
> GMP/bcmath/ext-decimal functionality is pointless and a waste of time.
>

Again, the use cases for arbitrary precision numbers seems rather limited,
and having this as an extension does not seem problematic at all.
My current issue is that there is no way to represent "small" numbers such
as 0.1 or 5.8 exactly.
Arbitrary precision is a totally different ballpark, be that for integers
and/or fractional values and makes everything slow, just look at Python who
is *finally* investing time and money to make the most common numbers
(those that fit in a machine word) not be abysmally slow.


> [...]
>



> How quickly you would be able to read something like this:
> gmp_add(gmp_mul(gmp_div(gmp_sub($value, $add), $value2, $factor,
> gmp_sin($value3), gmp_intval($value))) ?
>
This absence of intuitive readability makes the development and support
> difficult and it's better to choose another language. This is what I want
> to change and make PHP powerful enough for this kind of applications.
>

GMP supports operator overloading, so you do not need to write this, and as
far as I see, ext/decimal *also* supports operator overloading so you can
just write it using the normal arithmetic operations.
Moreover, it supports type casts so you can just do (int) $GMP instead of
gmp_intval($GMP);
And it also supports using the comparisons operatos on it instead of using
gmp_cmp()

The only thing that is, currently, not possible to do is a cast _to_ GMP
using (GMP), but maybe that's something that could be added to the engine
for internal objects, but I'm not sure about this.

It seems, your main motivation to have this as a language feature is to
have access to operator overloading and casting behaviour, that internal
objects already support.
Which diminish the cost/benefit of making the engine changes, especially if
the default behaviour doesn't change and one needs to maintain effectively
two different versions of PHP depending on if it is in "decimal" or
"floating" mode, which raises a plethora of questions just in its own.

Best regards,

Gina P. Banyard

---

## #121990 — G. P. B. — 2023-12-12T13:04:46+00:00

URL: https://externals.io/message/121938#121990 · In reply to: #121988

Gina P. Banyard

On Tue, 12 Dec 2023 at 08:51, Alexander Pravdin <alex.pravdin@interi.co>
wrote:

> I just found out that under PHP 8.3, basic arithmetic operations on the
> Decimal object variables are no longer supported and cause TypeError. So
> this is one more point to implementing native decimals in PHP.
>

This is either a bug in the extension or a bug in PHP 8.3 (which I doubt)
but I will have a look at this.

Gina P. Banyard

---

## #121991 — Robert Landers — 2023-12-12T20:59:57+00:00

URL: https://externals.io/message/121938#121991 · In reply to: #121989

>
> On Fri, 8 Dec 2023 at 10:14, Alexander Pravdin <alex.pravdin@interi.co>
> wrote:
>
> > […]
>
> Internal objects can overload casts, SimpleXML overloads the boolean cast
> already.
> We could add this to GMP to return false for 0.
>
>
> > […]
>
> I meant converting floats to int.
> But you can lose precision when converting an integer to float as there are
> only 53 bits for the coefficient.
>
>
> > […]
>
> The issue is that I don't think having arbitrary precision decimals as a
> core language feature is a necessity compared to rational types.
> A cast from rational to float wouldn't produce a large round trip, whereas
> trying to figure out arbitrary precision is more difficult.
> But in any case, having a declare/INI or whatever that changes the
> behaviour of the engine/language is not a good design choice.
>
>
> > […]
>
> Again, the use cases for arbitrary precision numbers seems rather limited,
> and having this as an extension does not seem problematic at all.
> My current issue is that there is no way to represent "small" numbers such
> as 0.1 or 5.8 exactly.
> Arbitrary precision is a totally different ballpark, be that for integers
> and/or fractional values and makes everything slow, just look at Python who
> is *finally* investing time and money to make the most common numbers
> (those that fit in a machine word) not be abysmally slow.
>
>
> > […]
>
>
>
> > […]
> This absence of intuitive readability makes the development and support
> > […]
>
> GMP supports operator overloading, so you do not need to write this, and as
> far as I see, ext/decimal *also* supports operator overloading so you can
> just write it using the normal arithmetic operations.
> Moreover, it supports type casts so you can just do (int) $GMP instead of
> gmp_intval($GMP);
> And it also supports using the comparisons operatos on it instead of using
> gmp_cmp()
>
> The only thing that is, currently, not possible to do is a cast _to_ GMP
> using (GMP), but maybe that's something that could be added to the engine
> for internal objects, but I'm not sure about this.
>
> It seems, your main motivation to have this as a language feature is to
> have access to operator overloading and casting behaviour, that internal
> objects already support.
> Which diminish the cost/benefit of making the engine changes, especially if
> the default behaviour doesn't change and one needs to maintain effectively
> two different versions of PHP depending on if it is in "decimal" or
> "floating" mode, which raises a plethora of questions just in its own.
>
> Best regards,
>
> Gina P. Banyard

Hey Gina,

> GMP supports operator overloading

GMP kinda-sorta-most-of-the-time supports operator overloading.
Sometimes ... it doesn't. I implemented a field library in PHP (for
work a couple of years ago) and occasionally, overloading would cast
things back to float/int and break the math. I don't have access to
that code, so I don't have any examples readily available (and the
fact that an extension can do overloading but we can't in user-land is
a whole different can of worms which made this library ridiculously
hard to work with -- we rewrote everything in Scala and never looked
back). Needless to say, if I were to go into a project that required
GMP, I wouldn't trust the overloading.

Just my 2¢

Robert Landers
Software Engineer
Utrecht NL

---

## #121992 — Larry Garfield — 2023-12-12T21:25:44+00:00

URL: https://externals.io/message/121938#121992 · In reply to: #121989

> The issue is that I don't think having arbitrary precision decimals as a
> core language feature is a necessity compared to rational types.
> A cast from rational to float wouldn't produce a large round trip, whereas
> trying to figure out arbitrary precision is more difficult.
> But in any case, having a declare/INI or whatever that changes the
> behaviour of the engine/language is not a good design choice.

I don't have strong feelings about arbitrary precision decimals either way, but I do strongly agree with this point.  Having the language behavior (including the precision of numbers) change with an ini setting is long-understood to be a bad idea, and we've been trying to phase it out.  A decimal feature that relies on a language-affecting ini setting is just not going to fly these days, IMO, and rightly so.

I am curious, GB, if you're proposing an actual `rational` type, which stores values internally as just numerator and denominator separately until some point when it renders down to a float (eg, on print)?  That sounds neat, though I am nowhere near expert enough in that area to say what ugly edge cases that might run into.

--Larry Garfield

---

## #121993 — Jordan LeDoux — 2023-12-12T21:30:07+00:00

URL: https://externals.io/message/121938#121993 · In reply to: #121991

On Tue, Dec 12, 2023 at 1:00 PM Robert Landers <landers.robert@gmail.com>
wrote:

>
> Hey Gina,
>
> > […]
>
> GMP kinda-sorta-most-of-the-time supports operator overloading.
> Sometimes ... it doesn't. I implemented a field library in PHP (for
> work a couple of years ago) and occasionally, overloading would cast
> things back to float/int and break the math. I don't have access to
> that code, so I don't have any examples readily available (and the
> fact that an extension can do overloading but we can't in user-land is
> a whole different can of worms which made this library ridiculously
> hard to work with -- we rewrote everything in Scala and never looked
> back). Needless to say, if I were to go into a project that required
> GMP, I wouldn't trust the overloading.
>
> Just my 2¢
>

I tried not to bring that up in this discussion because it would probably
sound bitter/unhelpful, but the lack of userland operator overloading is
definitely something that makes a native decimal scalar more necessary.
Voters have repeatedly rejected userland operator overloads. The last
proposal was mine, and most of the people who voted not told me that they
didn't have any real problems with the design, they just don't like
operator overloads.

Things like ext-decimal and GMP are always going to be clunky and annoying
to use without them though. It's one of the reasons I considered writing my
own implementation of a decimal scalar for core after my operator overload
RFC was declined. The use case for a scalar decimal type is much stronger
since nothing like it can be remotely implemented in userland at all. (I
know from experience, I wrote from scratch the most comprehensive arbitrary
precision math library in PHP.)

Jordan

---

## #121994 — Jordan LeDoux — 2023-12-12T21:34:38+00:00

URL: https://externals.io/message/121938#121994 · In reply to: #121992

On Tue, Dec 12, 2023 at 1:26 PM Larry Garfield <larry@garfieldtech.com>
wrote:

> 
>
> > […]
> whereas
> > […]
>
> I don't have strong feelings about arbitrary precision decimals either
> way, but I do strongly agree with this point.  Having the language behavior
> (including the precision of numbers) change with an ini setting is
> long-understood to be a bad idea, and we've been trying to phase it out.  A
> decimal feature that relies on a language-affecting ini setting is just not
> going to fly these days, IMO, and rightly so.
>
> I am curious, GB, if you're proposing an actual `rational` type, which
> stores values internally as just numerator and denominator separately until
> some point when it renders down to a float (eg, on print)?  That sounds
> neat, though I am nowhere near expert enough in that area to say what ugly
> edge cases that might run into.
>
> --Larry Garfield
>

I agree that an INI setting or declare is wrong for this.

The ugly edge cases on a native rational type tend to be in complex
algorithms, which many libraries deal with by converting the rational to a
float or decimal for those complex algorithms and just avoiding the issue.
(Algorithms like `sin()`, `atan()`, or `exp()`). It's not an unsolveable
problem if you really want rational types. There are libraries that do it.
But unless there's someone here volunteering, it's probably a non-starter,
as that's something I won't touch.

Jordan

---

## #121995 — Erick de Azevedo Lima — 2023-12-12T21:52:30+00:00

URL: https://externals.io/message/121938#121995 · In reply to: #121994

Hi Jordan.

What's the name of the library you're talking about? Maybe the cons of a
core implementation can be highlighted if we can see the limitations of a
user-land approach.

Best,
Erick

Em ter., 12 de dez. de 2023 às 18:35, Jordan LeDoux <jordan.ledoux@gmail.com>
escreveu:

> On Tue, Dec 12, 2023 at 1:26 PM Larry Garfield <larry@garfieldtech.com>
> wrote:
>
> > […]
> a
> > […]
> behavior
> > […]
> out.  A
> > […]
> not
> > […]
> until
> > […]
> ugly
> > […]
>
> I agree that an INI setting or declare is wrong for this.
>
> The ugly edge cases on a native rational type tend to be in complex
> algorithms, which many libraries deal with by converting the rational to a
> float or decimal for those complex algorithms and just avoiding the issue.
> (Algorithms like `sin()`, `atan()`, or `exp()`). It's not an unsolveable
> problem if you really want rational types. There are libraries that do it.
> But unless there's someone here volunteering, it's probably a non-starter,
> as that's something I won't touch.
>
> Jordan

---

## #121996 — Erick de Azevedo Lima — 2023-12-12T23:04:33+00:00

URL: https://externals.io/message/121938#121996 · In reply to: #121995

Oh, I just realized that I used the wrong word, so let me rephrase that:

What's the name of the library you're talking about? Maybe the *pros* of a
core implementation can be highlighted if we can see the limitations of a
user-land approach.

Best,
Erick

Em ter., 12 de dez. de 2023 às 18:52, Erick de Azevedo Lima <
ericklima.comp@gmail.com> escreveu:

> Hi Jordan.
>
> What's the name of the library you're talking about? Maybe the cons of a
> core implementation can be highlighted if we can see the limitations of a
> user-land approach.
>
> Best,
> Erick
>
> Em ter., 12 de dez. de 2023 às 18:35, Jordan LeDoux <
> jordan.ledoux@gmail.com> escreveu:
>
> > […]

---

## #121997 — Jordan LeDoux — 2023-12-13T00:29:22+00:00

URL: https://externals.io/message/121938#121997 · In reply to: #121996

On Tue, Dec 12, 2023 at 3:05 PM Erick de Azevedo Lima <
ericklima.comp@gmail.com> wrote:

> Oh, I just realized that I used the wrong word, so let me rephrase that:
>
> What's the name of the library you're talking about? Maybe the *pros* of a
> core implementation can be highlighted if we can see the limitations of a
> user-land approach.
>
> Best,
> Erick
>
>
My library is Fermat: https://github.com/JordanRL/Fermat

Mine is the ONLY one that I'm aware of that even attempts to support
complex mathematical operations in PHP. However, there are other arbitrary
precision math libraries that are more limited in scope.

Other similar math libraries:

markrogoyski/math-php: The closest in terms of features compared to Fermat
brick/math: The most widely installed PHP math library on Packagist (due to
being a dependency of several other widely used packages). Has extremely
limited features for complex arbitrary precision functions.

MPDecimal (the underlying C library that we have discussed using here) has
extensions and functions for things like sine and cosine. Even just
exposing those would make writing algorithms for things like 3i^(2+5i)
much, much faster.

The problem is that there's no point in using arbitrary precision numbers
unless you're using the spaces after the decimal. I think we can all agree
on that. No matter how good the implementation, floats are always going to
be faster if you're unconcerned about rounding errors and such.

So if you're writing an arbitrary precision library and you want to support
the next obvious operation beyond add, subtract, multiply, and divide, you
probably naturally choose exponents. Except, calculating A^B where both A
and B have *arbitrary precision* and where the result will also have
*arbitrary precision* means that you cannot rely on things like the `pow()`
function. So you have to implement your own. Except implementing decimal
exponents means you *really* need to implement `exp()` first, since all of
the efficient algorithms rely on transforming A^B into C*e^D. Except the
most efficient ways of doing *that* really work best if you have access to
trig functions. And *ALL* your calculations in the middle can't be losing
precision, otherwise there's no point in doing your own implementation, so
that means all of these functions need to have implementations as well.

This rabbit hole is *so* insidious that the BCMath library simple says
"fuck it" and limits you to only using integers for your exponents. That's
basically pointless though if the reason you need arbitrary precision
numbers is something like a statistics application for instance, which PHP
*also* lacks support for since the one extension that used to provide
statistics functions is no longer maintained and hasn't been updated since
I think 7.4.

If we get scalar decimals that only support add, subtract, multiply, and
divide, that's a huge improvement. But if we can take this opportunity to
tackle *at least* the trig functions as well, that will enable a LOT of new
userland code that is very messy and very slow right now.

Jordan

---

## #121998 — Jordan LeDoux — 2023-12-13T00:33:06+00:00

URL: https://externals.io/message/121938#121998 · In reply to: #121997

On Tue, Dec 12, 2023 at 4:29 PM Jordan LeDoux <jordan.ledoux@gmail.com>
wrote:

>
>
> On Tue, Dec 12, 2023 at 3:05 PM Erick de Azevedo Lima <
> ericklima.comp@gmail.com> wrote:
>
> > […]
> My library is Fermat: https://github.com/JordanRL/Fermat
>
> Mine is the ONLY one that I'm aware of that even attempts to support
> complex mathematical operations in PHP. However, there are other arbitrary
> precision math libraries that are more limited in scope.
>
> Other similar math libraries:
>
> markrogoyski/math-php: The closest in terms of features compared to Fermat
> brick/math: The most widely installed PHP math library on Packagist (due
> to being a dependency of several other widely used packages). Has extremely
> limited features for complex arbitrary precision functions.
>
> MPDecimal (the underlying C library that we have discussed using here) has
> extensions and functions for things like sine and cosine. Even just
> exposing those would make writing algorithms for things like 3i^(2+5i)
> much, much faster.
>
> The problem is that there's no point in using arbitrary precision numbers
> unless you're using the spaces after the decimal. I think we can all agree
> on that. No matter how good the implementation, floats are always going to
> be faster if you're unconcerned about rounding errors and such.
>
> So if you're writing an arbitrary precision library and you want to
> support the next obvious operation beyond add, subtract, multiply, and
> divide, you probably naturally choose exponents. Except, calculating A^B
> where both A and B have *arbitrary precision* and where the result will
> also have *arbitrary precision* means that you cannot rely on things like
> the `pow()` function. So you have to implement your own. Except
> implementing decimal exponents means you *really* need to implement `exp()`
> first, since all of the efficient algorithms rely on transforming A^B into
> C*e^D. Except the most efficient ways of doing *that* really work best if
> you have access to trig functions. And *ALL* your calculations in the
> middle can't be losing precision, otherwise there's no point in doing your
> own implementation, so that means all of these functions need to have
> implementations as well.
>
> This rabbit hole is *so* insidious that the BCMath library simple says
> "fuck it" and limits you to only using integers for your exponents. That's
> basically pointless though if the reason you need arbitrary precision
> numbers is something like a statistics application for instance, which PHP
> *also* lacks support for since the one extension that used to provide
> statistics functions is no longer maintained and hasn't been updated since
> I think 7.4.
>
> If we get scalar decimals that only support add, subtract, multiply, and
> divide, that's a huge improvement. But if we can take this opportunity to
> tackle *at least* the trig functions as well, that will enable a LOT of new
> userland code that is very messy and very slow right now.
>
> Jordan
>

Apologies, I just realized that you were asking about the libraries that
implemented rationals with complex operations. I don't have THOSE libraries
handy unfortunately. Even my library converts to decimal first.

Jordan

---

## #121999 — Stephen Reay — 2023-12-13T08:37:18+00:00

URL: https://externals.io/message/121938#121999 · In reply to: #121938

> 
> 
> 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


Hi,

While I do think it would be beneficial to have a built-in `decimal` type, regarding ext-decimal, I think some of the points raised may be outdated?

The documentation site seems to have moved to https://php-decimal.github.io <https://php-decimal.github.io/>, which also indicates that it implements operator overrides.


Cheers

Stephen

---

## #122000 — Robert Landers — 2023-12-13T09:11:45+00:00

URL: https://externals.io/message/121938#122000 · In reply to: #121999

>
>
>
> > […]
>
>
> Hi,
>
> While I do think it would be beneficial to have a built-in `decimal` type, regarding ext-decimal, I think some of the points raised may be outdated?
>
> The documentation site seems to have moved to https://php-decimal.github.io <https://php-decimal.github.io/>, which also indicates that it implements operator overrides.
>
>
> Cheers
>
> Stephen
>
>

Hello Stephen,

I just ran `apt install php8.3-decimal` and tried this:

$a = new Decimal\Decimal("1", 2);
$b = $a + $a;
PHP Warning:  Uncaught TypeError: Unsupported operand types:
Decimal\Decimal + Decimal\Decimal in

So, it appears not.

---

## #122001 — Alexander Pravdin — 2023-12-13T09:29:26+00:00

URL: https://externals.io/message/121938#122001 · In reply to: #122000

On Wed, Dec 13, 2023 at 6:11 PM Robert Landers <landers.robert@gmail.com>
wrote:

I just ran `apt install php8.3-decimal` and tried this:
>
> $a = new Decimal\Decimal("1", 2);
> $b = $a + $a;
> PHP Warning:  Uncaught TypeError: Unsupported operand types:
> Decimal\Decimal + Decimal\Decimal in
>
> So, it appears not.
>

I've pointed out this issue earlier in the discussion and Gina P. Banyard
has replied that this can be a PHP 8.3 bug and he will look into it.

BTW, my main concern is that this amazing extension doesn't look like it is
actively maintained and I'm worrying if I will be able to maintain my
projects that depend on it in the future.

Best,
Alex

---

## #122005 — G. P. B. — 2023-12-15T01:09:48+00:00

URL: https://externals.io/message/121938#122005 · In reply to: #121991

On Tue, 12 Dec 2023 at 21:00, Robert Landers <landers.robert@gmail.com>
wrote:

> 
> > […]
>
> GMP kinda-sorta-most-of-the-time supports operator overloading.
> Sometimes ... it doesn't. I implemented a field library in PHP (for
> work a couple of years ago) and occasionally, overloading would cast
> things back to float/int and break the math. I don't have access to
> that code, so I don't have any examples readily available (and the
> fact that an extension can do overloading but we can't in user-land is
> a whole different can of worms which made this library ridiculously
> hard to work with -- we rewrote everything in Scala and never looked
> back). Needless to say, if I were to go into a project that required
> GMP, I wouldn't trust the overloading.
>

I have no idea how _this_ is possible considering GMP will happily throw
type errors left and right even in cases when it shouldn't.
If you encountered this, you should have submitted a bug report.
Because, using a potential bug as an excuse for not doing this is... weird?

I have come around userland operator overloading with the proposal from
Jordan, but considering this hasn't passed it might take a while before
someone gives it a crack at it again.
And it has _always_ been a thing that the engine, and internal extensions,
can do more things than userland.
Especially nonsensical stuff like variadic parameters not at the end...

Gina P. Banyard

---

## #122006 — G. P. B. — 2023-12-15T01:17:57+00:00

URL: https://externals.io/message/121938#122006 · In reply to: #122001

On Wed, 13 Dec 2023 at 09:29, Alexander Pravdin <alex.pravdin@interi.co>
wrote:

> On Wed, Dec 13, 2023 at 6:11 PM Robert Landers <landers.robert@gmail.com>
> wrote:
>
> I just ran `apt install php8.3-decimal` and tried this:
> > […]
>
> I've pointed out this issue earlier in the discussion and Gina P. Banyard
> has replied that this can be a PHP 8.3 bug and he will look into it.
>
> BTW, my main concern is that this amazing extension doesn't look like it is
> actively maintained and I'm worrying if I will be able to maintain my
> projects that depend on it in the future.
>

Hot take, why not fund the maintainer (or someone else to fork and pick up
the work) to work on this extension?
It is not like most bundled extension within php-src are truly actively
maintained.
So why would integrating ext/decimal mean it will be maintained?
Just have a look at all the XML/DOM related extensions that were riddled
with bugs and unmaintained until Nils decided to pick up the work to
scratch an itch.

Moreover, being separate from "core" is frankly a benefit, and I very much
dislike the current status of having so many bundled extensions that cannot
improve at their own pace.
Having extensions not be tied to php-src's release cycle means they can
move more quickly, fix bugs faster, evolve more easily, etc.
Just look at ext/curl, being stuck at a certain level of support for
libcurl because it *must* be tied to the annual PHP release cycle.

Best regards,

Gina P. Banyard

PS: My pronouns are she/her

---

## #122007 — Robert Landers — 2023-12-15T08:14:11+00:00

URL: https://externals.io/message/121938#122007 · In reply to: #122005

>
> 
> > […]

Hey Gina,

> I have no idea how _this_ is possible considering GMP will happily throw type errors left and right even in cases when it shouldn't.
> If you encountered this, you should have submitted a bug report.
> Because, using a potential bug as an excuse for not doing this is... weird?

The issue should have been reproducible on a smaller scale, but it
wasn't, thereby hinting that we were hitting some extremely rare edge
case that may not be easily testable. The fact that we hit it,
couldn't create a simple reproduction for reporting a bug, and you
should, theoretically, be able to rely on your software to compute
math correctly made that one person's argument: "Why don't we rewrite
this in X?" that much more persuasive...

> I have come around userland operator overloading with the proposal from Jordan, but considering this hasn't passed it might take a while before someone gives it a crack at it again.
> And it has _always_ been a thing that the engine, and internal extensions, can do more things than userland.
> Especially nonsensical stuff like variadic parameters not at the end...

Considering operator overloading has been declined twice (in 2021:
https://wiki.php.net/rfc/user_defined_operator_overloads, and 2020
https://wiki.php.net/rfc/userspace_operator_overloading) for "reasons"
has me thinking it probably won't be proposed again. With comments
like:

> it is not your implementation proposal,
> but rather the concept per-se that IMO shouldn't land in the language

nobody will likely ever try this again, at least in the foreseeable
future. With comments like that, there is simply no way forward.
There's no convincing (at least via email) and by that point, it's too
late anyway, they already voted but didn't even show up for the
discussion that happened for weeks. Literally wasting everyone's time.
The only way we'd ever get something like this passed is if someone
proposes an RFC that prevents people from voting based on
political/personal reasons and restricts voting "no" to technical
reasons only; or some voters reopen one of the original RFC's for a
revote and leave it in the "Pending Implementation" section as needing
an implementation.

The fact that people can and do vote "no" for no other reason than
they "don't like it" or they "don't want to use it" leaves a bad taste
in my mouth.

Robert Landers
Software Engineer
Utrecht NL

---

## #122008 — Jordan LeDoux — 2023-12-15T20:31:56+00:00

URL: https://externals.io/message/121938#122008 · In reply to: #122007

On Fri, Dec 15, 2023 at 12:14 AM Robert Landers <landers.robert@gmail.com>
wrote:

>
> nobody will likely ever try this again, at least in the foreseeable
> future. With comments like that, there is simply no way forward.
> There's no convincing (at least via email) and by that point, it's too
> late anyway, they already voted but didn't even show up for the
> discussion that happened for weeks. Literally wasting everyone's time.
> The only way we'd ever get something like this passed is if someone
> proposes an RFC that prevents people from voting based on
> political/personal reasons and restricts voting "no" to technical
> reasons only; or some voters reopen one of the original RFC's for a
> revote and leave it in the "Pending Implementation" section as needing
> an implementation.
>
> The fact that people can and do vote "no" for no other reason than
> they "don't like it" or they "don't want to use it" leaves a bad taste
> in my mouth.
>

Okay, so I'm probably the most affected by this, considering we're
discussing my proposal which was declined and I spent over 400 hours of
work on it prior to the vote. So please understand where I'm coming from
when I say this: I firmly believe that people should be allowed to vote no
on an RFC simply because they feel that it "doesn't belong in PHP". That is
a voter taking a position on what the language is for, how it should be
designed from a more broad perspective, and I think it's important to have
that in order to maintain a productive language that does its job well.

The main issue I have discovered through this experience is that some
people have difficulty or lack the experience necessary to separate an
opinion about the language design philosophy from what would be personally
useful to *them*. That is a problem, but it's a problem that is just part
of working collaboratively with humans. It's not unique to this group, to
PHP, or to this situation.

The reason, for everyone else reading, that this is relevant to the current
discussion about a scalar decimal type is that this is likely to face some
similar pushback. This email will go out to hundreds of people, but only a
few are going to reply, and most of the people currently participating in
this discussion do not have voting rights, so this RFC if it's worked on
will have to be developed without the feedback of the people who actually
decide whether it goes into the language.

A scalar decimal type will be extremely useful for certain types of
applications, foundationally critical to other types of applications, and
completely unused for others. A big part of writing this RFC would be
explaining to the people who do not work on any applications that benefit
why it would be good to include it in the language anyway.

A scalar decimal type would create a hard dependency on libmpdec, which I
also expect to be something that needs to be addressed. MPDec is licensed
under the Simplified BSD License. I *think* it could be bundled if needed,
but it couldn't be relicensed with the PHP License itself.

Jordan

---

Next page: https://externals.io/message/121938.md?page=2
