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=phpinternals&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 typehinting.
 Can use PHP's basic type coercions.
Extdecimal:
 Thirdparty 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 phpdecimal 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 humanreadable 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
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=phpinternals&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 typehinting.
 Can use PHP's basic type coercions.
Extdecimal:
 Thirdparty 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 phpdecimal 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 floatIt 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 humanreadable 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.5This 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 conversionAll 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 64bit, 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 arbitrarylength 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 arbitraryprecision 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
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
anint
, no matter how long it is.For example, this multiplies the max 64bit, signed integer by 2, and the
result is still anint
:>>> 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
>>> x9223372036854775807922337203685477580792233720368547758079223372036854775807
>>> type(x)
<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 arbitrarylength integers, is there a reason we
couldn’t also have arbitrary length floating point numbers?
I'm not familiar with arbitrarylength 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 arbitraryprecision integers and floats,
and it would be seamless to the user, who would see onlyint
andfloat
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 lowlevel
manipulation of numbers.
Best regards,
Alex.
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=phpinternals&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, extdecimal, 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
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, extdecimal, 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()
oratan()
. 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 fortan()
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 functionsin()
isn't
capable of outputting adecimal
, 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.
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 nogo 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.5This 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 conversionAll 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 nonarbitrary IEEE 7542008 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 32bit unsigned integer for the numerator and an 32bit
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
 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.
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.
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
humanreadable 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 nogo 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
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.
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
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.
I didn't know that the strict types directive was a mistake. My intention
is to be able to write clean alldecimal 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
alldecimal 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
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.5This is a kind of "default_decimal" for a specific operation.
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.
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 conversionAll 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.
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.
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 nonarbitrary IEEE 7542008 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 32bit unsigned integer for the numerator and an 32bit
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/extdecimal functionality is pointless and a waste of time.
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.
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 wellknown 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.
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 incorrect, GMP object do not support casts to bool
See https://3v4l.org/LHpD1This 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
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.
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
humanreadable 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?
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
TypeError is already a nogo 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
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.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
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.I didn't know that the strict types directive was a mistake. My intention
is to be able to write clean alldecimal 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
alldecimal 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
(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 nonarbitrary IEEE 7542008 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 32bit unsigned integer for the numerator and an
32bit 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/extdecimal 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
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 incorrect, GMP object do not support casts to bool
See https://3v4l.org/LHpD1This 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
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.
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
humanreadable 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?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
TypeError is already a nogo 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
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.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
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.I didn't know that the strict types directive was a mistake. My intention
is to be able to write clean alldecimal 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
alldecimal 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
(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 nonarbitrary IEEE 7542008 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 32bit unsigned integer for the numerator and an
32bit 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/extdecimal 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
Hey Gina,
GMP supports operator overloading
GMP kindasortamostofthetime 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 userland 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
On Tue, Dec 12, 2023 at 1:00 PM Robert Landers landers.robert@gmail.com
wrote:
Hey Gina,
GMP supports operator overloading
GMP kindasortamostofthetime 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 userland 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 extdecimal 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
On Tue, 12 Dec 2023 at 21:00, Robert Landers landers.robert@gmail.com
wrote:
GMP supports operator overloading
GMP kindasortamostofthetime 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 userland 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
GMP supports operator overloading
GMP kindasortamostofthetime 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 userland 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.
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 perse 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
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
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
Jordan has highlighted good points that probably should be addressed and in
general, I think, this project will take a while, so I would settle in for
the long haul here and think things through and reach out to people who
actively work and maintain the PHP core. There have been recent instances
of people fitting in and passing things and then regretting the way it was
done (cought readonly rfc cought ).
As for the idea itself  I fully support it. A decimal type would be
invaluable in quite a lot of places and a lot of PHP apps are handling
money and other precise numbers that really would benefit from a proper
decimal type (right now in most cases people just do it with integers or
use libraries like brick/money, but that's only a subset covered).
This is really adding a new type into the engine, so this is going to be a
big undertaking and this probably will require the work to go into PHP 9
because extensions probably are going to be affected and will need to be
upgraded to handle the new type. Especially PDO and database drivers. This
is far from a small project.

Arvīds Godjuks
+371 26 851 664
arvids.godjuks@gmail.com
Telegram: @psihius https://t.me/psihius
On Fri, Dec 15, 2023 at 12:59 PM Arvids Godjuks arvids.godjuks@gmail.com
wrote:
On Fri, 15 Dec 2023 at 22:32, Jordan LeDoux jordan.ledoux@gmail.com
wrote: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
Jordan has highlighted good points that probably should be addressed and
in general, I think, this project will take a while, so I would settle in
for the long haul here and think things through and reach out to people who
actively work and maintain the PHP core. There have been recent instances
of people fitting in and passing things and then regretting the way it was
done (cought readonly rfc cought ).As for the idea itself  I fully support it. A decimal type would be
invaluable in quite a lot of places and a lot of PHP apps are handling
money and other precise numbers that really would benefit from a proper
decimal type (right now in most cases people just do it with integers or
use libraries like brick/money, but that's only a subset covered).
This is really adding a new type into the engine, so this is going to be a
big undertaking and this probably will require the work to go into PHP 9
because extensions probably are going to be affected and will need to be
upgraded to handle the new type. Especially PDO and database drivers. This
is far from a small project.
Here's the research that I did into this subject when I was considering
making my own decimal scalar proposal about a year ago.
MPDec: This is used by extdecimal. It's licensed with the Simplified BSD
License. It has functions for basic arithmetic (add, subtract, multiply,
divide), as well as logarithmic functions (ln, log10, exp, power, square
root). It does not have functions for trigonometric operations, and has
some limitations for where arbitrary precision numbers can be used (for
instance with pow).
GMP: This is the underlying library of several other math libraries in C.
It is dual licensed with the LGPL 3 and GPL 2 licenses. It has functions
for basic arithmetic.
MPFR: This is an extension of the GMP library that supports a much wider
variety of math. It is licensed with the LGPL. It supports arithmetic,
logarithmic, trigonometric, inverse trigonometric, hyperbolic, and number
theory operations (such as the gamma function which extends the factorial
operation beyond integers).
FLINT: This is a library built on top of MPFR (or rather, it depends on
MPFR). It is licensed with the LGPL 2. It supports everything that MPFR
does, with additional support for complex numbers, expressions (such as
polynomials and exponentials), derivatives and integrals, additional number
theory operations (such as generating Bernoulli numbers), and other
features.
I think all of these could be bundled.
MPDec will not supply all of the features that have been discussed in this
mailing list thread. The minimal library that does is probably MPFR.
A zval has to fit in 64 bits, which none of these will do, so our scalar
decimal type would probably have to be refcounted the same way that objects
are. To have it behave like other scalars would probably involve some very
tricky work implementing efficient copying into scopes. Otherwise, decimal
scalars would passbypointer in the same way objects do where they ignore
function scope isolation to avoid copying large amounts of memory for
temporary scope changes. Having it behave like an int or float zval would
require either a big change to how zvals work, or something rather creative
with pointers and scope based copyonwrite with garbage collection to
clean things up.
While the arbitrary precision types would not fit in a zval, they would be
much smaller than objects are in PHP (generally), so something creative
might be worth at least exploring. I think the kind of radical change that
would be necessary for them to fit entirely in a zval instead of just
fitting the pointer in a zval are likely to be a nonstarter. I would
imagine it would reduce the performance of other zval optimizations.
However, the specifics of zval implementation is something that I am not at
all an expert in, so take my research on that front with a grain of salt.
Jordan
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 longunderstood to be a bad idea, and we've been trying to phase it out. A decimal feature that relies on a languageaffecting 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
On Tue, Dec 12, 2023 at 1:26 PM Larry Garfield larry@garfieldtech.com
wrote:
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
longunderstood to be a bad idea, and we've been trying to phase it out. A
decimal feature that relies on a languageaffecting 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 nonstarter,
as that's something I won't touch.
Jordan
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
userland 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: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
longunderstood to be a bad idea, and we've been trying to phase it
out. A
decimal feature that relies on a languageaffecting 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 likesin()
,atan()
, orexp()
). 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 nonstarter,
as that's something I won't touch.Jordan
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
userland 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
userland approach.Best,
ErickEm 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: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
longunderstood to be a bad idea, and we've been trying to phase it
out. A
decimal feature that relies on a languageaffecting 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 likesin()
,atan()
, orexp()
). 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 nonstarter,
as that's something I won't touch.Jordan
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
userland 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/mathphp: 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
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: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
userland approach.Best,
ErickMy 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/mathphp: 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
thepow()
function. So you have to implement your own. Except
implementing decimal exponents means you really need to implementexp()
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
Extdecimal:
...
 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
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
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=phpinternals&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 typehinting.
 Can use PHP's basic type coercions.
Extdecimal:
 Thirdparty 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 phpdecimal 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 floatIt 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 humanreadable 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.5This 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 conversionAll 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 builtin decimal
type, regarding extdecimal, I think some of the points raised may be outdated?
The documentation site seems to have moved to https://phpdecimal.github.io https://phpdecimal.github.io/, which also indicates that it implements operator overrides.
Cheers
Stephen
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=phpinternals&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 typehinting.
 Can use PHP's basic type coercions.
Extdecimal:
 Thirdparty 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 phpdecimal 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 floatIt 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 humanreadable 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.5This 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 conversionAll 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 PravdinHi,
While I do think it would be beneficial to have a builtin
decimal
type, regarding extdecimal, I think some of the points raised may be outdated?The documentation site seems to have moved to https://phpdecimal.github.io https://phpdecimal.github.io/, which also indicates that it implements operator overrides.
Cheers
Stephen
Hello Stephen,
I just ran apt install php8.3decimal
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.
On Wed, Dec 13, 2023 at 6:11 PM Robert Landers landers.robert@gmail.com
wrote:
I just ran apt install php8.3decimal
and tried this:
$a = new Decimal\Decimal("1", 2);
$b = $a + $a;
PHP Warning: Uncaught TypeError: Unsupported operand types:
Decimal\Decimal + Decimal\Decimal inSo, 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
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.3decimal
and tried this:$a = new Decimal\Decimal("1", 2);
$b = $a + $a;
PHP Warning: Uncaught TypeError: Unsupported operand types:
Decimal\Decimal + Decimal\Decimal inSo, 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.
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 phpsrc 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 phpsrc'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