Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:121940 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 35731 invoked from network); 7 Dec 2023 07:37:16 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 7 Dec 2023 07:37:16 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id C5D4618003C for ; Wed, 6 Dec 2023 23:37:28 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-3.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,RCVD_IN_MSPIKE_H2, SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-yw1-f181.google.com (mail-yw1-f181.google.com [209.85.128.181]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 6 Dec 2023 23:37:28 -0800 (PST) Received: by mail-yw1-f181.google.com with SMTP id 00721157ae682-5d8a772157fso3322947b3.3 for ; Wed, 06 Dec 2023 23:37:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=benramsey.com; s=google; t=1701934633; x=1702539433; darn=lists.php.net; h=message-id:in-reply-to:to:references:date:subject:mime-version :content-transfer-encoding:from:from:to:cc:subject:date:message-id :reply-to; bh=p1ueGGO/Ebsxietf9PY4AdSB+U9UxsbBsU4VdbX6hY0=; b=dQwq52c8sfCoyPaXamX6eZ9RGe9b0AieOGJtDUiV/KhfwG04lhwCzVazsyqVLPJhkK AOXUDF4T4ZVTTbI/5zK30agvTS6PmYcjnA/VMprf2pEiaIzYsEuqepQiKpqcSfROckS1 Qil1d+L5BD5tIVGBGIXFMr2Rm9o2AmF6ClEsflyKoKfKp+VdMAnUzDpi7Kwn9jWFZlZw wFtS5B6cBKFJYWrwn/wH8I+PynIygo2kz/2ntXrd83XP/G6GHpL8QfA4CNirmNURiPvC adH0B8XOZlT4YUc+gXeZ3a6i7tSOBKBKJ0N0B8B5E9qNa0/KSKLh+SmG5yAn8yDP7HcC wACA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701934633; x=1702539433; h=message-id:in-reply-to:to:references:date:subject:mime-version :content-transfer-encoding:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=p1ueGGO/Ebsxietf9PY4AdSB+U9UxsbBsU4VdbX6hY0=; b=lCudChHxBvwTCshlPBGCIaB4t8MKPcei7KRrUcScubgOGKy8a3jmsMtVzGVzjFYFu6 MtOISMLFLrD4ZusMYVOrjwkkwWmzu37AOBgUH3dtN1Tb3a70rm9JVtRrmG7C1KLH1T/F 0xFvLJpPeDbQROQK896UpfXkHuh9XwhkSz5MbQvgdpPrksJLdgvygn+dwTcYXQAplo0c 51WOwnT481UNoEcgUjviw6irnjOrdY6KBU9tURaDeS/msktBfw1QF138hu2+ihYN3P// +3t4nL/UP249ECCe0taWNeca/+ng1hZzFwu3Pbomm/JDw3+mCDkZewh1Ie+umJm/DItB D6+Q== X-Gm-Message-State: AOJu0Yxtich4ip7FP212Rxbh9gDAkTu8t7GBBXaIvTV5o6gOZrFcEPlz Uyv3oa52xbJNyqlGUSmMvvXxbR8lYCCIjkfotyk= X-Google-Smtp-Source: AGHT+IE+CcSWDJCuDPXDzETZx8X1liXVyTCpizjy9MEqw6nDHkVoDuZ//QZJ59mIBARUpl5Wnt4XBQ== X-Received: by 2002:a81:b104:0:b0:5d7:1941:ac3 with SMTP id p4-20020a81b104000000b005d719410ac3mr1919740ywh.94.1701934633466; Wed, 06 Dec 2023 23:37:13 -0800 (PST) Received: from smtpclient.apple (h96-61-170-179.lvrgtn.broadband.dynamic.tds.net. [96.61.170.179]) by smtp.gmail.com with ESMTPSA id r6-20020a0de806000000b005d580a1fd70sm212323ywe.75.2023.12.06.23.37.12 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 06 Dec 2023 23:37:12 -0800 (PST) Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3774.200.91.1.1\)) Date: Thu, 7 Dec 2023 01:37:01 -0600 References: <18c42fdbb30.2831.17a3710df6d58f02ca570cc47e197a63@interi.co> To: PHP Developers Mailing List In-Reply-To: <18c42fdbb30.2831.17a3710df6d58f02ca570cc47e197a63@interi.co> Message-ID: X-Mailer: Apple Mail (2.3774.200.91.1.1) Subject: Re: [PHP-DEV] Proposal: Arbitrary precision native scalar type From: ben@benramsey.com (Ben Ramsey) > On Dec 7, 2023, at 00:36, Alex Pravdin wrote: >=20 > Hello internals, >=20 >=20 > This is the second round of the discussion regarding arbitrary = precision scalar type integration into PHP. The previous part: = https://marc.info/?l=3Dphp-internals&m=3D168250492216838&w=3D2 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. >=20 > Today's alternatives and their problems are the following. >=20 > bcmath: > - Workaround: using string type. > - Unintuitive function calls instead of regular math operations. > - Unintuitive strings instead of numbers. People want to work with = numbers. > - Can not use proper type-hinting. > - Can use PHP's basic type coercions. >=20 > Ext-decimal: > - Third-party extension. > - Workaround: implements the Decimal class that allows basic regular = math operations. > - Requires using class methods for the rest of math operations. > - The latest release was in 2019 and there's a danger that it will be = unmaintained and not compatible with the future PHP releases. > - The php-decimal documentation website is currently down. > - Since objects are always casted to true when not null, "(bool) = Decimal(0)" will equal to true which is not intuitive. > - IDEs are often confused when you use math operations on objects = while the code works fine. >=20 > GMP: > - Workaround: implements the GMP class that allows basic math = operations. > - Requires using separate functions for the rest of operations. >=20 > - Objects are always casted to true, GMP(0) will equal to true. >=20 >=20 > 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. >=20 >=20 > Decimal values can be created from literals by specifying a modifier = or using the (decimal) typecast: >=20 > $v =3D 0.2d; > $v =3D (decimal) 0.2; // Creates a decimal value without intermediary = float >=20 > It uses the precision and scale defined in php.ini. >=20 > 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. >=20 > New functions added: get_scale and get_precision to return = corresponding values about a decimal value. >=20 > 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". >=20 > 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". >=20 > Decimal to float conversion is allowed and smooth: >=20 > function f (float $value) {} >=20 > f(0.2); >=20 > f(0.2d); // allowed with no warnings >=20 >=20 > Function "str_to_decimal" added to convert string representation of = numbers to decimals. >=20 >=20 > Typecast from string to decimal works the same as the "str_to_decimal" = function. >=20 > Function "float_to_decimal" added to explicitly convert floats to = decimals. It performs float to string conversions using php.ini settings = as defaults but also accepts parameters to configure the conversion. = Then, it converts string to decimal. Since the main problem of float to = decimal conversion is that we don't know the exact result until we use = some rounding when transforming it to a human-readable format, it looks = like the step of the conversion to a string is inevitable. Any more = optimized algorithms are welcome. >=20 > 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. >=20 > 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. >=20 > 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. >=20 > New language construct "as_decimal()" is added to produce decimal math = results for literals and math operations instead of float without = intermediary float: >=20 > $var =3D 5 / 2; // returns float 2.5 > $var =3D as_decimal(5 / 2); // returns decimal 2.5 >=20 > This is a kind of "default_decimal" for a specific operation. >=20 > 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): >=20 > $f =3D (float) 0.2; > $d =3D (decimal) 0.2; >=20 > $r =3D $f + $d; // returns float result by default > $r =3D as_decimal($f + $d); // returns decimal result with a warning = about implicit float to decimal conversion >=20 > 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. >=20 >=20 > The new type uses libmpdec internally to perform decimal calculations = (same as Python). >=20 >=20 > All of the points above are subject to discussions, it is not an RFC = candidate right now. So please share your opinions. >=20 > 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. >=20 >=20 > -- > 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=E2=80=99m not sure how = they store the values internally, but to users of Python, any sequence = of numbers is an `int`, no matter how long it is. For example, this multiplies the max 64-bit, signed integer by 2, and = the result is still an `int`: >>> i =3D 9223372036854775807 * 2 >>> i 18446744073709551614 >>> type(i) And this is a really, really, really long number that=E2=80=99s still an = `int`: >>> x =3D = 92233720368547758079223372036854775807922337203685477580792233720368547758= 07 >>> x = 92233720368547758079223372036854775807922337203685477580792233720368547758= 07 >>> type(x) Python has floating point numbers, but these use the double type in C, = so they=E2=80=99re not arbitrary in length, like its integers. (Python = uses libmpdec, as you=E2=80=99ve mentioned, with `decimal.Decimal`.) However, if we can achieve arbitrary-length integers, is there a reason = we couldn=E2=80=99t also have arbitrary length floating point numbers? Below a certain threshold, we could use the system=E2=80=99s native int = and double for storage and math. Above a certain threshold, we could = internally switch to a bundled library for handling arbitrary-precision = integers and floats, and it would be seamless to the user, who would see = only `int` and `float` types in PHP. Anyway, this is something I=E2=80=99ve been thinking about for some = time, but I haven=E2=80=99t taken the time to look into what it might = take to implement it. It sounds like you have looked into it, so I=E2=80=99= m interested to know whether you think what I=E2=80=99ve described here = is feasible. Cheers, Ben Cheers, Ben