From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS4713 221.184.0.0/13 X-Spam-Status: No, score=-2.8 required=3.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, FORGED_GMAIL_RCVD,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, SPF_HELO_NONE,SPF_PASS shortcircuit=no autolearn=no autolearn_force=no version=3.4.2 Received: from neon.ruby-lang.org (neon.ruby-lang.org [221.186.184.75]) by dcvr.yhbt.net (Postfix) with ESMTP id 7AAC41F97E for ; Mon, 29 Jul 2019 16:40:55 +0000 (UTC) Received: from neon.ruby-lang.org (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id 7893C120AA2; Tue, 30 Jul 2019 01:40:47 +0900 (JST) Received: from o1678916x28.outbound-mail.sendgrid.net (o1678916x28.outbound-mail.sendgrid.net [167.89.16.28]) by neon.ruby-lang.org (Postfix) with ESMTPS id D3493120A6B for ; Tue, 30 Jul 2019 01:40:44 +0900 (JST) Received: by filter0080p3mdw1.sendgrid.net with SMTP id filter0080p3mdw1-8190-5D3F218E-45 2019-07-29 16:40:46.876393049 +0000 UTC m=+251303.636387358 Received: from herokuapp.com (unknown [3.91.185.73]) by ismtpd0061p1mdw1.sendgrid.net (SG) with ESMTP id a1cM0aB7RXCRk0i-_sVJeA for ; Mon, 29 Jul 2019 16:40:46.812 +0000 (UTC) Date: Mon, 29 Jul 2019 16:40:46 +0000 (UTC) From: wishdev@gmail.com Message-ID: References: Mime-Version: 1.0 X-Redmine-MailingListIntegration-Message-Ids: 69504 X-Redmine-Project: ruby-trunk X-Redmine-Issue-Id: 15811 X-Redmine-Issue-Author: yennguyenh X-Redmine-Sender: wishdev X-Mailer: Redmine X-Redmine-Host: bugs.ruby-lang.org X-Redmine-Site: Ruby Issue Tracking System X-Auto-Response-Suppress: All Auto-Submitted: auto-generated X-SG-EID: =?us-ascii?Q?Z4Ej7Bg37lK69JtZPFJ+UW67gqYKj9Iu8E0xhUBC8OWKWtQ7CxPgP71FIHZH25?= =?us-ascii?Q?UnDIA1l6KXzv7tLZnWdh8iBpfmaE+ItDMY4pHH3?= =?us-ascii?Q?T+=2FY0La9o0BjCovkB1Q4=2FhdmGGj8IueO79hpkG2?= =?us-ascii?Q?AVoSRhaX8MyBesj1+hz1UBFYpGmADghfEfTTDvA?= =?us-ascii?Q?nmB9Sm4Gx5z+q+7M+FcY=2FCW2d2Ss=2FrYswOA=3D=3D?= To: ruby-core@ruby-lang.org X-ML-Name: ruby-core X-Mail-Count: 93999 Subject: [ruby-core:93999] [Ruby master Feature#15811] Propsing new method for comparing equality of 2 (float) numbers relatively X-BeenThere: ruby-core@ruby-lang.org X-Mailman-Version: 2.1.15 Precedence: list Reply-To: Ruby developers List-Id: Ruby developers List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ruby-core-bounces@ruby-lang.org Sender: "ruby-core" Issue #15811 has been updated by wishdev (John Higgins). So there is one path that might work here (it has limits but there are limits in general that cannot be worked around). If one takes the following scenario We have a set of items in a store where the base unit of price is 0.01 meaning that something may cost 1.23, or 2.45, but we would not see anything like 1.234 or 45.4555 because those are lower than the working "unit of change" (0.01). So we could construct a method like this which would allow us to determine if two items where priced within a set of "units of change" def with_amount(item_a, item_b, within_amount, unit_of_change) (item_a - item_b).abs < ((within_amount).abs + (unit_of_change).abs /2) end So going back to earlier examples - if we take 0.01 vs 0.02 we would have a unit of change of no more than 0.01 because while we might have 0.015 as a valid option - we know that 0.03 is a valid option. So taking the new method item_a = 0.01 item_b = 0.02 within_amount = 0.01 unit_of_change = 0.01 (0.01 - 0.02).abs = 0.01 (0.01).abs + (0.01).abs /2 = 0.015 0.01 < 0.015 so it returns true This works because we know that the unit of change implies the limits of the possible answers of (item_a - item_b).abs - so in this case we should have the following options 0.01, 0.02, 0.03, and so on Since floating point subtraction is not exact we might end up with 0.0099999999994747, 0.01, or 0.010000000001 when comparing two adjacent numbers within our set However, if we take the unit of change and halve it then we have better boundaries 0.010000000001 does not worked against a tight comparison to 0.01 but it works just fine if we compare it against 0.015 or 0.005 because those options lay well outside of the error bounds that floating point subtraction will offer us. So the answer remains that simply comparing two numbers does not work - however, one can compare two numbers within a defined space and obtain the desired conceptual results. The obvious rules are that within_amount must be at least equal to the unit of change. There are also issues if the within_amount is not a multiple of the unit of change. The validation check for that (checking if x is a multiple of y) starts us back down the road of inexact comparisons so it may be something that is a documented limit as opposed to a test within the method itself. John ---------------------------------------- Feature #15811: Propsing new method for comparing equality of 2 (float) numbers relatively https://bugs.ruby-lang.org/issues/15811#change-80199 * Author: yennguyenh (yen nguyen) * Status: Open * Priority: Normal * Assignee: * Target version: ---------------------------------------- # Background Equal comparison method between 2 float numbers returns unexpected results sometimes. Therefore, a relative comparison method is needed! # Proposal A relative equal comparison method has been written based on a Python project! This method gives the approximation for the equal comparison based on two values: realative tolerance and absolute tolerance. Near zero value will also be considered carefully! # Implementation The function for that would be called close? `close?(a, b, rel_tol, abs_tol)` `a` and `b`: are the two values to be tested to relative closeness `rel_tol`: is the relative tolerance -- it is the amount of error allowed, relative to the larger absolute value of a or b. For example, to set a tolerance of 5%, pass tol=0.05. The default tolerance is 1E-9, which assures that the two values are the same within about 9 decimal digits. rel_tol must be greater than 0.0 `abs_tol`: is a minimum absolute tolerance level -- useful for comparisons near zero. # Evaluation of your implementation By default, relative tolerance is 1E-9 which is relatively precise enough to compare two float numbers. However it can also be adjusted in case higher accuracy is requested. The absolute tolerance is by default 0.0 and need to be set in case of near-zero numbers. # Discussion There are some test cases available for the method which has approved the accuracy of the method. BigNumbers and integers are also tested. However, more test cases are still needed to assure even better the accuracy of the method. # Gist Relative equal comparison https://gist.github.com/yennguyenh/63d5e7a11f354f796b43ada037c4b2c5 Test cases https://gist.github.com/yennguyenh/2e81dc72b310cb9d886a82faf3d536ef -- https://bugs.ruby-lang.org/