Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:119214 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 18735 invoked from network); 29 Dec 2022 13:28:56 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 29 Dec 2022 13:28:56 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 308B0180507 for ; Thu, 29 Dec 2022 05:28:56 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE,SPF_PASS, T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS24940 176.9.0.0/16 X-Spam-Virus: No X-Envelope-From: Received: from chrono.xqk7.com (chrono.xqk7.com [176.9.45.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Thu, 29 Dec 2022 05:28:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bastelstu.be; s=mail20171119; t=1672320535; bh=zgNpcFiHs2iCQTz1Ay6Fb2AcWPxZT7PH2XtdeP6yMdQ=; h=Date:Subject:To:Cc:References:From:In-Reply-To:From; b=AyVBVVxLnlNYrQa35wqDOWfS06yCOvSh72KHTxAaiiR7aQimSBa/WViHza0HTrYxd kX0hBOIG6fm9CpadR3f/spBKj+VOa+P9Ek0D1QuC3CM+GEDJclXvtSTO4h6hc4RWRL Fc7bfLWJIg0TAWX15ig5MHm7ejyIH4mqC4ZHFAxXje81I80C0WpTXqz0YcE/QZ7uxb ZGTyG402TzF6nVFmamU6uyxb3/mY0Gf5rzE9tDvrldDF2e0nxsKMvE7IXqLeaf0E18 z3ptzIljqy7RbaiMDnLupCcMQK4SJ++2RiRLaNjipDD4Fv6a79k4O6xH611EqjdN4Q f4HGmTj5z1bFw== Message-ID: <91e74741-3841-bdda-5331-e5175e825d44@bastelstu.be> Date: Thu, 29 Dec 2022 14:28:54 +0100 MIME-Version: 1.0 Content-Language: en-US To: Hans Henrik Bergan , Go Kudo Cc: internals@lists.php.net References: In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Subject: Re: [PHP-DEV] ext-random: add random_float() ? From: tim@bastelstu.be (=?UTF-8?Q?Tim_D=c3=bcsterhus?=) Hi On 12/20/22 09:15, Hans Henrik Bergan wrote: > btw while we're on the topic, does anyone know if this function gives > biased results/is-safe or not? i honestly don't know: > function random_float(float $min, float $max): float > { > if ($min > $max) throw new \InvalidArgumentException("min must be > less than max"); > if ($min === $max) return $min; > return $min + random_int(0, PHP_INT_MAX) / PHP_INT_MAX * ($max - $min); > } > > To give a clear answer on this one: For a full evaluation the reader lacks information: What is this function supposed to do? For floats, the definition of the boundaries matter (that's why it's a parameter for Randomizer::getFloat()). Your function is implemented on the closed interval [0, 1] (i.e. both including 0 and 1). As mentioned in my email a few minutes ago, I consider the ClosedClosed behavior to not be the most useful in the general case, but in your case it might be the correct behavior. Nonetheless, the function is definitely broken: 1. A IEEE 754 double precision float as used by PHP only has 53 Bits of precision, but you generate a random 64 Bit integer. Thus rounding will occur and might cause some values to be returned more often than other. To improve the function you should only generate 53 Bits of randomness (i.e. use 2**53 as the divisor and either 2**53-1 or 2**53 as the dividend). 2. Using the (min + rand * (max - min)) construction will generate biased results (see also my other email). PHP 8.3's Randomizer::getFloat() method uses the γ-section algorithm proposed by Prof. Goualard to generate unbiased results for arbitrary ranges. This algorithm cannot reasonably be implemented in userland, because the necessary "building blocks" (obtaining the next/previous float for a given float) are not available for userland code. The best you can do in PHP 8.2 userland is what Randomizer::nextFloat() does. TLDR: Yes, the function is broken. Best regards Tim Düsterhus