Hello everyone. To continue the discussion, I'm suggesting an updated version of my proposal. The main change is: to use decimal128 as the backend for native decimals, so no 3rd-party libraries are required. Even if adopting a library, it looks like it'll be much easier than the previous proposal. Also, the principle "if any operand is decimal, the result is decimal" is adopted, and conversion requirements are relaxed and more specific.
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 Native decimals are backed by 128-bit decimals according to IEEE 754-2008. Point to discuss: can we use 128-bit zvals or transpile decimals into a pair of 64-bit zvals representing lo and hi portions? Decimal to float conversion is allowed and smooth: function f (float $value) {} f(0.2); f(0.2d); // allowed with no warnings or errors Function "str_to_decimal" added to convert from string representation of numbers to decimals. Typecast from string to decimal works the same as the "str_to_decimal" function. The function "float_to_decimal" is added to explicitly convert floats to decimals. Internally, it performs explicit float to string conversions using existing defaults and also accepts an optional parameter to define the number of fractional digits to round to. After converting a float to a string, it converts the string to a decimal. Since the main problem of float to decimal conversion is that we don't know the exact result until we use some rounding when transforming it to a human-readable format, it looks like the step of the conversion to a string is inevitable. Any optimized algorithms are welcome. Explicit type cast "(decimal) float" is the same as using float_to_decimal with defaults. Implicit conversion from float to decimal with type juggling works the same as "float_to_decimal" with defaults but throws a warning to encourage users to use explicit conversion instead: "Implicit float to decimal conversion may cause unexpected results due to possible rounding errors". With strict types, implicit conversion is not possible and generates a TypeError. Literal numbers in the code are compiled to floats by default. If prepended by the "(decimal)" typecast, or the "d" modifier is used (0.2d) the decimal is produced by the compiler without an intermediary float or string. 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. With default_decimal, fractional string literals are compiled into decimals. Without default_decimal: $var = 5 / 2; // returns float 2.5 $a = 3.02; // compiled to float With default_decimal: $var = 5 / 2; // returns decimal 2.5 $a = 3.02; // compiled to decimal I understand that this point is controversial, so it can be postponed and decided later. The (decimal) typecast applied to a math operation produces a decimal result without intermediary float by converting all operands to decimals before calculating: $var = 5 / 2; // returns float 2.5 $var = (decimal)(5 / 2); // returns decimal 2.5 $a = 5; $b = 2; $var = (decimal)($a / $b); // returns decimal 2.5 If any of the math operation operands are decimal, the result is decimal. Floats are converted to decimals with implicit conversion rules mentioned above: Without strict types: $f = (float) 0.2; $d = (decimal) 0.2; $r = $f + $d; // produces a warning about implicit conversion, returns decimal $r = (decimal)$f + $d; // works with no warnings With strict types: $f = (float) 0.2; $d = (decimal) 0.2; $r = $f + $d; // produces TypeError $r = (decimal)$f + $d; // works with no warnings 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 any of the parameters is decimal, they return decimal. Float parameters are converted to decimals implicitly according to the conversion rules mentioned above. I hope this version of the design is closer to being accepted by the community. Please share your thoughts. -- Best, Alexander -- Best regards, Alex Pravdin Interico On Wed, Apr 10, 2024 at 12:55 AM Alexander Pravdin <alex.prav...@interi.co> wrote: > > On Tue, Apr 9, 2024 at 7:52 PM Derick Rethans <der...@php.net> wrote: > > > Adding a new native type to PHP will create a large change. Not only is > > it "just" adding a new native type, it also means all of the conversions > > between types need to be added. This is not a small task. > > I understand this :) > > > > If you want to use arbitrary precision natives, then a precision and > > scale as defined in php.ini defeats the purpose. Every installation can > > then potentially calculate things in a different way. > > > > The only way how to prevent that, is to have *actual* Decimal type, such > > as the Decimal type in MongoDB uses (the IEEE 754 decimal128 type): > > > > - > > https://www.mongodb.com/docs/mongodb-shell/reference/data-types/#std-label-shell-type-decimal > > - https://en.wikipedia.org/wiki/Decimal128_floating-point_format > > If PHP core experts think that 128-bit decimal will cover the vast > majority of cases and is worth implementing, I'm totally for it. If we > can implement something standardized then fine. The current thread is > not an RFC candidate, but a kinda discussion board to formulate the > design principles and strategy. > > On another note, is it possible to make zval variable size - 64 or 128 > bits? So the 128-bit decimal can be a struct that will be held in the > stack instead of pointer manipulations. Can it be achieved with the > help of macroses? > > -- > Best, Alexander