Approximate equality
You are encouraged to solve this task according to the task description, using any language you may know.
Sometimes, when testing whether the solution to a task (for example, here on Rosetta Code) is correct, the difference in floating point calculations between different language implementations becomes significant.
For example, a difference between 32 bit and 64 bit floating point calculations may appear by about the 8th significant digit in base 10 arithmetic.
- Task
Create a function which returns true if two floating point numbers are approximately equal.
The function should allow for differences in the magnitude of numbers, so that, for example,
100000000000000.01 may be approximately equal to 100000000000000.011,
even though 100.01 is not approximately equal to 100.011.
If the language has such a feature in its standard library, this may be used instead of a custom function.
Show the function results with comparisons on the following pairs of values:
- 100000000000000.01, 100000000000000.011 (note: should return true)
- 100.01, 100.011 (note: should return false)
- 10000000000000.001 / 10000.0, 1000000000.0000001000
- 0.001, 0.0010000001
- 0.000000000000000000000101, 0.0
- sqrt(2) * sqrt(2), 2.0
- -sqrt(2) * sqrt(2), -2.0
- 3.14159265358979323846, 3.14159265358979324
Answers should be true for the first example and false in the second, so that just rounding the numbers to a fixed number of decimals should not be enough. Otherwise answers may vary and still be correct. See the Python code for one type of solution.
AWK
<lang AWK>
- syntax: GAWK -f APPROXIMATE_EQUALITY.AWK
- converted from C#
BEGIN {
epsilon = 1 while (1 + epsilon != 1) { epsilon /= 2 } printf("epsilon = %18.16g\n\n",epsilon) main("100000000000000.01","100000000000000.011") main("100.01","100.011") main("10000000000000.001"/"10000.0","1000000000.0000001000") main("0.001","0.0010000001") main("0.000000000000000000000101","0.0") main(sqrt(2.0)*sqrt(2.0),"2.0") main(-sqrt(2.0)*sqrt(2.0),"-2.0") main("3.14159265358979323846","3.14159265358979324") exit(0)
} function main(a,b, tmp) {
tmp = abs(a - b) < epsilon printf("%d %27s %s\n",tmp,a,b)
} function abs(x) { if (x >= 0) { return x } else { return -x } } </lang>
- Output:
epsilon = 1.110223024625157e-016 1 100000000000000.01 100000000000000.011 0 100.01 100.011 0 1e+009 1000000000.0000001000 0 0.001 0.0010000001 1 0.000000000000000000000101 0.0 0 2 2.0 0 -2 -2.0 1 3.14159265358979323846 3.14159265358979324
C
<lang c>#include <math.h>
- include <stdbool.h>
- include <stdio.h>
bool approxEquals(double value, double other, double epsilon) {
return fabs(value - other) < epsilon;
}
void test(double a, double b) {
double epsilon = 1e-18; printf("%f, %f => %d\n", a, b, approxEquals(a, b, epsilon));
}
int main() {
test(100000000000000.01, 100000000000000.011); test(100.01, 100.011); test(10000000000000.001 / 10000.0, 1000000000.0000001000); test(0.001, 0.0010000001); test(0.000000000000000000000101, 0.0); test(sqrt(2.0) * sqrt(2.0), 2.0); test(-sqrt(2.0) * sqrt(2.0), -2.0); test(3.14159265358979323846, 3.14159265358979324); return 0;
}</lang>
- Output:
100000000000000.015625, 100000000000000.015625 => 1 100.010000, 100.011000 => 0 1000000000.000000, 1000000000.000000 => 0 0.001000, 0.001000 => 0 0.000000, 0.000000 => 1 2.000000, 2.000000 => 0 -2.000000, -2.000000 => 0 3.141593, 3.141593 => 1
C#
<lang csharp>using System;
public static class Program {
public static void Main() { Test(100000000000000.01, 100000000000000.011); Test(100.01, 100.011); Test(10000000000000.001 / 10000.0, 1000000000.0000001000); Test(0.001, 0.0010000001); Test(0.000000000000000000000101, 0.0); Test(Math.Sqrt(2) * Math.Sqrt(2), 2.0); Test(-Math.Sqrt(2) * Math.Sqrt(2), -2.0); Test(3.14159265358979323846, 3.14159265358979324);
void Test(double a, double b) { const double epsilon = 1e-18; WriteLine($"{a}, {b} => {a.ApproxEquals(b, epsilon)}"); } }
public static bool ApproxEquals(this double value, double other, double epsilon) => Math.Abs(value - other) < epsilon;
}</lang>
- Output:
100000000000000.02, 100000000000000.02 => True 100.01, 100.011 => False 1000000000.0000002, 1000000000.0000001 => False 0.001, 0.0010000001 => False 1.01E-22, 0 => True 2.0000000000000004, 2 => False -2.0000000000000004, -2 => False 3.141592653589793, 3.141592653589793 => True
C++
<lang cpp>#include <iomanip>
- include <iostream>
bool approxEquals(double a, double b, double e) {
return fabs(a - b) < e;
}
void test(double a, double b) {
constexpr double epsilon = 1e-18; std::cout << std::setprecision(21) << a; std::cout << ", "; std::cout << std::setprecision(21) << b; std::cout << " => "; std::cout << approxEquals(a, b, epsilon) << '\n';
}
int main() {
test(100000000000000.01, 100000000000000.011); test(100.01, 100.011); test(10000000000000.001 / 10000.0, 1000000000.0000001000); test(0.001, 0.0010000001); test(0.000000000000000000000101, 0.0); test(sqrt(2.0) * sqrt(2.0), 2.0); test(-sqrt(2.0) * sqrt(2.0), -2.0); test(3.14159265358979323846, 3.14159265358979324); return 0;
}</lang>
- Output:
100000000000000.015625, 100000000000000.015625 => 1 100.010000000000005116, 100.01099999999999568 => 0 1000000000.00000023842, 1000000000.00000011921 => 0 0.00100000000000000002082, 0.00100000010000000005492 => 0 1.0099999999999999762e-22, 0 => 1 2.00000000000000044409, 2 => 0 -2.00000000000000044409, -2 => 0 3.141592653589793116, 3.141592653589793116 => 1
D
<lang d>import std.math; import std.stdio;
auto approxEquals = (double a, double b, double epsilon) => abs(a - b) < epsilon;
void main() {
void test(double a, double b) { double epsilon = 1e-18; writefln("%.18f, %.18f => %s", a, b, a.approxEquals(b, epsilon)); }
test(100000000000000.01, 100000000000000.011); test(100.01, 100.011); test(10000000000000.001 / 10000.0, 1000000000.0000001000); test(0.001, 0.0010000001); test(0.000000000000000000000101, 0.0); test(sqrt(2.0) * sqrt(2.0), 2.0); test(-sqrt(2.0) * sqrt(2.0), -2.0); test(3.14159265358979323846, 3.14159265358979324);
}</lang>
- Output:
100000000000000.015620000000000000, 100000000000000.015620000000000000 => true 100.010000000000005110, 100.010999999999995680 => false 1000000000.000000119100000000, 1000000000.000000119100000000 => true 0.001000000000000000, 0.001000000100000000 => false 0.000000000000000000, 0.000000000000000000 => true 2.000000000000000000, 2.000000000000000000 => true -2.000000000000000000, -2.000000000000000000 => true 3.141592653589793116, 3.141592653589793116 => true
Factor
The ~
word takes three arguments: the two values to be compared, and an epsilon value representing the allowed distance between the two values. A positive epsilon performs an absolute distance test, an epsilon of zero performs an exact comparison, and a negative epsilon performs a relative distance test (as required by this task).
<lang factor>USING: formatting generalizations kernel math math.functions ;
100000000000000.01 100000000000000.011 100.01 100.011 10000000000000.001 10000.0 /f 1000000000.0000001000 0.001 0.0010000001 0.000000000000000000000101 0.0 2 sqrt dup * 2.0 2 sqrt dup neg * -2.0 3.14159265358979323846 3.14159265358979324
[ 2dup -1e-15 ~ "%+47.30f %+47.30f -1e-15 ~ : %u\n" printf ] 2 8 mnapply</lang>
- Output:
+100000000000000.015625000000000000000000000000 +100000000000000.015625000000000000000000000000 -1e-15 ~ : t +100.010000000000005115907697472721 +100.010999999999995679900166578591 -1e-15 ~ : f +1000000000.000000238418579101562500000000 +1000000000.000000119209289550781250000000 -1e-15 ~ : t +0.001000000000000000020816681712 +0.001000000100000000054917270731 -1e-15 ~ : f +0.000000000000000000000101000000 +0.000000000000000000000000000000 -1e-15 ~ : f +2.000000000000000444089209850063 +2.000000000000000000000000000000 -1e-15 ~ : t -2.000000000000000444089209850063 -2.000000000000000000000000000000 -1e-15 ~ : t +3.141592653589793115997963468544 +3.141592653589793115997963468544 -1e-15 ~ : t
Fortran
Compare against the Python function documented at https://www.python.org/dev/peps/pep-0485/#proposed-implementation, and with the discussion at https://stackoverflow.com/questions/5595425/what-is-the-best-way-to-compare-floats-for-almost-equality-in-python# <lang fortran>program main
implicit none
integer :: i double precision, allocatable :: vals(:)
vals = [ 100000000000000.01d0, 100000000000000.011d0, & & 100.01d0, 100.011d0, & & 10000000000000.001d0/10000d0, 1000000000.0000001000d0, & & 0.001d0, 0.0010000001d0, & & 0.000000000000000000000101d0, 0d0, & & sqrt(2d0)*sqrt(2d0), 2d0, & & -sqrt(2d0)*sqrt(2d0), -2d0, & & 3.14159265358979323846d0, 3.14159265358979324d0 ]
do i = 1, size(vals)/2 print '(ES30.18, A, ES30.18, A, L)', vals(2*i-1), ' == ', vals(2*i), ' ? ', eq_approx(vals(2*i-1), vals(2*i)) end do
contains
logical function eq_approx(a, b, reltol, abstol) !! is a approximately equal b?
double precision, intent(in) :: a, b !! values to compare double precision, intent(in), optional :: reltol, abstol !! relative and absolute error thresholds. !! defaults: epsilon, smallest non-denormal number
double precision :: rt, at
rt = epsilon(1d0) at = tiny(1d0) if (present(reltol)) rt = reltol if (present(abstol)) at = abstol
eq_approx = abs(a - b) .le. max(rt * max(abs(a), abs(b)), at) return end function
end program</lang>
- Output:
1.000000000000000156E+14 == 1.000000000000000156E+14 ? T 1.000100000000000051E+02 == 1.000109999999999957E+02 ? F 1.000000000000000238E+09 == 1.000000000000000119E+09 ? T 1.000000000000000021E-03 == 1.000000100000000055E-03 ? F 1.009999999999999976E-22 == 0.000000000000000000E+00 ? F 2.000000000000000444E+00 == 2.000000000000000000E+00 ? T -2.000000000000000444E+00 == -2.000000000000000000E+00 ? T 3.141592653589793116E+00 == 3.141592653589793116E+00 ? T
Go
Go's float64 type is limited to 15 or 16 digits of precision. As there are some numbers in this task which have more digits than this I've used big.Float instead. <lang go>package main
import (
"fmt" "log" "math/big"
)
func max(a, b *big.Float) *big.Float {
if a.Cmp(b) > 0 { return a } return b
}
func isClose(a, b *big.Float) bool {
relTol := big.NewFloat(1e-9) // same as default for Python's math.isclose() function t := new(big.Float) t.Sub(a, b) t.Abs(t) u, v, w := new(big.Float), new(big.Float), new(big.Float) u.Mul(relTol, max(v.Abs(a), w.Abs(b))) return t.Cmp(u) <= 0
}
func nbf(s string) *big.Float {
n, ok := new(big.Float).SetString(s) if !ok { log.Fatal("invalid floating point number") } return n
}
func main() {
root2 := big.NewFloat(2.0) root2.Sqrt(root2) pairs := [][2]*big.Float{ {nbf("100000000000000.01"), nbf("100000000000000.011")}, {nbf("100.01"), nbf("100.011")}, {nbf("0").Quo(nbf("10000000000000.001"), nbf("10000.0")), nbf("1000000000.0000001000")}, {nbf("0.001"), nbf("0.0010000001")}, {nbf("0.000000000000000000000101"), nbf("0.0")}, {nbf("0").Mul(root2, root2), nbf("2.0")}, {nbf("0").Mul(nbf("0").Neg(root2), root2), nbf("-2.0")}, {nbf("100000000000000003.0"), nbf("100000000000000004.0")}, {nbf("3.14159265358979323846"), nbf("3.14159265358979324")}, } for _, pair := range pairs { s := "≉" if isClose(pair[0], pair[1]) { s = "≈" } fmt.Printf("% 21.19g %s %- 21.19g\n", pair[0], s, pair[1]) }
}</lang>
- Output:
100000000000000.01 ≈ 100000000000000.011 100.01 ≉ 100.011 1000000000.0000001 ≈ 1000000000.0000001 0.001 ≉ 0.0010000001 1.01e-22 ≉ 0 2.000000000000000273 ≈ 2 -2.000000000000000273 ≈ -2 100000000000000003 ≈ 100000000000000004 3.141592653589793239 ≈ 3.14159265358979324
Groovy
<lang groovy>class Approximate {
private static boolean approxEquals(double value, double other, double epsilon) { return Math.abs(value - other) < epsilon }
private static void test(double a, double b) { double epsilon = 1e-18 System.out.printf("%f, %f => %s\n", a, b, approxEquals(a, b, epsilon)) }
static void main(String[] args) { test(100000000000000.01, 100000000000000.011) test(100.01, 100.011) test(10000000000000.001 / 10000.0, 1000000000.0000001000) test(0.001, 0.0010000001) test(0.000000000000000000000101, 0.0) test(Math.sqrt(2.0) * Math.sqrt(2.0), 2.0) test(-Math.sqrt(2.0) * Math.sqrt(2.0), -2.0) test(3.14159265358979323846, 3.14159265358979324) }
}</lang>
- Output:
100000000000000.020000, 100000000000000.020000 => true 100.010000, 100.011000 => false 1000000000.000000, 1000000000.000000 => true 0.001000, 0.001000 => false 0.000000, 0.000000 => true 2.000000, 2.000000 => false -2.000000, -2.000000 => false 3.141593, 3.141593 => true
J
Attributed to Ken Iverson, inventor of APL and of course his final dialect, j, "In an early talk Ken was explaining the advantages of tolerant comparison. A member of the audience asked incredulously, “Surely you don’t mean that when A=B and B=C, A may not equal C?” Without skipping a beat, Ken replied, “Any carpenter knows that!” and went on to the next question."
J includes a "customization" conjunction ( !. ) that delivers variants of some verbs. Comparisons are tolerant by default, and their tolerance can be customized to some level. Specifying =!.0 specifies "no tolerance". Specifying a tolerance of 1e_8 is a domain error because that's no longer math. Write your own verb if you need this. <lang>
NB. default comparison tolerance matches the python result ".;._2]0 :0 100000000000000.01 = 100000000000000.011 100.01 = 100.011 (10000000000000.001 % 10000.0) = 1000000000.0000001000 0.001 = 0.0010000001 0.000000000000000000000101 = 0.0 (= ([: *~ %:)) 2 NB. sqrt(2)*sqrt(2) ((= -)~ ([: (* -) %:)) 2 NB. -sqrt(2) * sqrt(2), -2.0 3.14159265358979323846 = 3.14159265358979324
) 1 0 1 0 0 1 1 1
NB. tolerance of 1e_12 matches the python result ".;._2]0 :0[CT=:1e_12 100000000000000.01 =!.CT 100000000000000.011 100.01 =!.CT 100.011 (10000000000000.001 % 10000.0) =!.CT 1000000000.0000001000 0.001 =!.CT 0.0010000001 0.000000000000000000000101 =!.CT 0.0 (=!.CT ([: *~ %:)) 2 NB. sqrt(2)*sqrt(2) ((=!.CT -)~ ([: (* -) %:)) 2 NB. -sqrt(2) * sqrt(2), -2.0 3.14159265358979323846 =!.CT 3.14159265358979324
) 1 0 1 0 0 1 1 1
NB. tight tolerance ".;._2]0 :0[CT=:1e_18 100000000000000.01 =!.CT 100000000000000.011 100.01 =!.CT 100.011 (10000000000000.001 % 10000.0) =!.CT 1000000000.0000001000 0.001 =!.CT 0.0010000001 0.000000000000000000000101 =!.CT 0.0 (=!.CT ([: *~ %:)) 2 NB. sqrt(2)*sqrt(2) ((=!.CT -)~ ([: (* -) %:)) 2 NB. -sqrt(2) * sqrt(2), -2.0 3.14159265358979323846 =!.CT 3.14159265358979324
) 1 0 0 0 0 0 0 1
2 (=!.1e_8) 9
|domain error | 2(= !.1e_8)9 </lang>
Java
<lang java>public class Approximate {
private static boolean approxEquals(double value, double other, double epsilon) { return Math.abs(value - other) < epsilon; }
private static void test(double a, double b) { double epsilon = 1e-18; System.out.printf("%f, %f => %s\n", a, b, approxEquals(a, b, epsilon)); }
public static void main(String[] args) { test(100000000000000.01, 100000000000000.011); test(100.01, 100.011); test(10000000000000.001 / 10000.0, 1000000000.0000001000); test(0.001, 0.0010000001); test(0.000000000000000000000101, 0.0); test(Math.sqrt(2.0) * Math.sqrt(2.0), 2.0); test(-Math.sqrt(2.0) * Math.sqrt(2.0), -2.0); test(3.14159265358979323846, 3.14159265358979324); }
}</lang>
- Output:
100000000000000.020000, 100000000000000.020000 => true 100.010000, 100.011000 => false 1000000000.000000, 1000000000.000000 => false 0.001000, 0.001000 => false 0.000000, 0.000000 => true 2.000000, 2.000000 => false -2.000000, -2.000000 => false 3.141593, 3.141593 => true
Julia
Julia has an infix operator, ≈, which corresponds to Julia's buitin isapprox() function.
<lang julia>testvalues = [[100000000000000.01, 100000000000000.011],
[100.01, 100.011], [10000000000000.001 / 10000.0, 1000000000.0000001000], [0.001, 0.0010000001], [0.000000000000000000000101, 0.0], [sqrt(2) * sqrt(2), 2.0], [-sqrt(2) * sqrt(2), -2.0], [3.14159265358979323846, 3.14159265358979324]]
for (x, y) in testvalues
println(rpad(x, 21), " ≈ ", lpad(y, 22), ": ", x ≈ y)
end
</lang>
- Output:
1.0000000000000002e14 ≈ 1.0000000000000002e14: true 100.01 ≈ 100.011: false 1.0000000000000002e9 ≈ 1.0000000000000001e9: true 0.001 ≈ 0.0010000001: false 1.01e-22 ≈ 0.0: false 2.0000000000000004 ≈ 2.0: true -2.0000000000000004 ≈ -2.0: true 3.141592653589793 ≈ 3.141592653589793: true
Kotlin
<lang scala>import kotlin.math.abs import kotlin.math.sqrt
fun approxEquals(value: Double, other: Double, epsilon: Double): Boolean {
return abs(value - other) < epsilon
}
fun test(a: Double, b: Double) {
val epsilon = 1e-18 println("$a, $b => ${approxEquals(a, b, epsilon)}")
}
fun main() {
test(100000000000000.01, 100000000000000.011) test(100.01, 100.011) test(10000000000000.001 / 10000.0, 1000000000.0000001000) test(0.001, 0.0010000001) test(0.000000000000000000000101, 0.0) test(sqrt(2.0) * sqrt(2.0), 2.0) test(-sqrt(2.0) * sqrt(2.0), -2.0) test(3.14159265358979323846, 3.14159265358979324)
}</lang>
- Output:
1.0000000000000002E14, 1.0000000000000002E14 => true 100.01, 100.011 => false 1.0000000000000002E9, 1.0000000000000001E9 => false 0.001, 0.0010000001 => false 1.01E-22, 0.0 => true 2.0000000000000004, 2.0 => false -2.0000000000000004, -2.0 => false 3.141592653589793, 3.141592653589793 => true
Lobster
<lang Lobster> // Return whether the two numbers `a` and `b` are close. // Closeness is determined by the `epsilon` parameter - // the numbers are considered close if the difference between them // is no more than epsilon * max(abs(a), abs(b)). // def isclose(a, b, epsilon):
return abs(a - b) <= max(abs(a), abs(b)) * epsilon
let tv = [
xy { 100000000000000.01, 100000000000000.011 }, xy { 100.01, 100.011 }, xy { 10000000000000.001 / 10000.0, 1000000000.0000001000 }, xy { 0.001, 0.0010000001 }, xy { 0.000000000000000000000101, 0.0 }, xy { sqrt(2.0) * sqrt(2.0), 2.0 }, xy { -sqrt(2.0) * sqrt(2.0), -2.0 }, xy { 3.14159265358979323846, 3.14159265358979324 } ]
for(tv) t:
print concat_string([string(t.x), if isclose(t.x, t.y, 1.0e-9): """ ≈ """ else: """ ≉ """, string(t.y)], "")
</lang>
- Output:
100000000000000.0 ≈ 100000000000000.0 100.01 ≉ 100.011 1000000000.0 ≈ 1000000000.0 0.001 ≉ 0.0010000001 0.0 ≉ 0.0 2.0 ≈ 2.0 -2.0 ≈ -2.0 3.14159265359 ≈ 3.14159265359
Perl
Passes task tests, but use the module Test::Number::Delta
for anything of real importance.
<lang perl>use strict;
use warnings;
sub is_close {
my($a,$b,$eps) = @_; $eps //= 15; my $epse = $eps; $epse++ if sprintf("%.${eps}f",$a) =~ /\./; $epse++ if sprintf("%.${eps}f",$a) =~ /\-/; my $afmt = substr((sprintf "%.${eps}f", $a), 0, $epse); my $bfmt = substr((sprintf "%.${eps}f", $b), 0, $epse); printf "%-5s %s ≅ %s\n", ($afmt eq $bfmt ? 'True' : 'False'), $afmt, $bfmt;
}
for (
[100000000000000.01, 100000000000000.011], [100.01, 100.011], [10000000000000.001 / 10000.0, 1000000000.0000001000], [0.001, 0.0010000001], [0.000000000000000000000101, 0.0], [sqrt(2) * sqrt(2), 2.0], [-sqrt(2) * sqrt(2), -2.0], [100000000000000003.0, 100000000000000004.0], [3.14159265358979323846, 3.14159265358979324] ) { my($a,$b) = @$_; is_close($a,$b);
}
print "\nTolerance may be adjusted.\n"; my $real_pi = 2 * atan2(1, 0); my $roman_pi = 22/7; is_close($real_pi,$roman_pi,$_) for <10 3>;</lang>
- Output:
True 100000000000000.0 ≅ 100000000000000.0 False 100.0100000000000 ≅ 100.0109999999999 True 1000000000.000000 ≅ 1000000000.000000 False 0.001000000000000 ≅ 0.001000000100000 True 0.000000000000000 ≅ 0.000000000000000 True 2.000000000000000 ≅ 2.000000000000000 True -2.000000000000000 ≅ -2.000000000000000 True 10000000000000000 ≅ 10000000000000000 True 3.141592653589793 ≅ 3.141592653589793 Tolerance may be adjusted. False 3.141592653 ≅ 3.142857142 True 3.14 ≅ 3.14
Phix
Traditionally I have always just used sprintf() to compare floating point atoms in phix.
This task (imo) is trying to make a general-purpose routine out of code (ie bool eq) which is best tailored for each and every specific task.
It proved much harder to get decent-looking output than perform the tests, hence I allowed both the compare (cfmt) and display (dfmt) formats to be overridden.
I got a different result for test 4 to everyone else, but simply setting the cfmt to "%.8f" got it the NOT.
Likewise something similar for the trickier/ambiguous test 5, and both now show how to get either a true or false result.
<lang Phix>procedure test(atom a,b, string dfmt="%g", cfmt="%g")
bool eq = sprintf(cfmt,a)==sprintf(cfmt,b) string eqs = iff(eq?"":"NOT "), sa = sprintf(dfmt,a), sb = sprintf(dfmt,b) printf(1,"%30s is %sapproximately equal to %s\n",{sa,eqs,sb})
end procedure
test(100000000000000.01,100000000000000.011,"%.3f") test(100.01,100.011) test(10000000000000.001/10000.0,1000000000.0000001000,"%.10f") test(0.001,0.0010000001,"%.8f") -- both test(0.001,0.0010000001,"%.8f","%.8f") -- ways test(0.000000000000000000000101,0.0,"%f") -- both test(0.000000000000000000000101,0.0,"%f","%6f") -- ways test(sqrt(2)*sqrt(2),2.0) test(-sqrt(2)*sqrt(2),-2.0) test(3.14159265358979323846,3.14159265358979324,"%.20f")</lang>
- Output:
64 bit (implied by some of the accuracies specified for this task):
100000000000000.010 is approximately equal to 100000000000000.011 100.01 is NOT approximately equal to 100.011 1000000000.0000001001 is approximately equal to 1000000000.0000001000 0.00100000000 is approximately equal to 0.0010000001 0.00100000000 is NOT approximately equal to 0.0010000001 0.000000000000000000000101000 is NOT approximately equal to 0.000000 0.000000000000000000000101000 is approximately equal to 0.000000 2 is approximately equal to 2 -2 is approximately equal to -2 3.14159265358979323851 is approximately equal to 3.14159265358979324003
32 bit (in fact a couple of them, the first and last pairs, are actually genuinely identical):
100000000000000.016 is approximately equal to 100000000000000.016 100.01 is NOT approximately equal to 100.011 1000000000.0000002384 is approximately equal to 1000000000.0000001192 0.0010000000 is approximately equal to 0.0010000001 0.0010000000 is NOT approximately equal to 0.0010000001 0.000000000000000000000101000 is NOT approximately equal to 0.000000 0.000000000000000000000101000 is approximately equal to 0.000000 2 is approximately equal to 2 -2 is approximately equal to -2 3.1415926535897931 is approximately equal to 3.1415926535897931
Python
The Python source documentation states:
math.isclose -> bool a: double b: double * rel_tol: double = 1e-09 maximum difference for being considered "close", relative to the magnitude of the input values abs_tol: double = 0.0 maximum difference for being considered "close", regardless of the magnitude of the input values Determine whether two floating point numbers are close in value. Return True if a is close in value to b, and False otherwise. For the values to be considered close, the difference between them must be smaller than at least one of the tolerances. -inf, inf and NaN behave similarly to the IEEE 754 Standard. That is, NaN is not close to anything, even itself. inf and -inf are only close to themselves.
<lang python>from numpy import sqrt from math import isclose
testvalues = [[100000000000000.01, 100000000000000.011],
[100.01, 100.011], [10000000000000.001 / 10000.0, 1000000000.0000001000], [0.001, 0.0010000001], [0.000000000000000000000101, 0.0], [sqrt(2) * sqrt(2), 2.0], [-sqrt(2) * sqrt(2), -2.0], [3.14159265358979323846, 3.14159265358979324]]
for (x, y) in testvalues:
maybenot = "is" if isclose(x, y) else "is NOT" print(x, maybenot, "approximately equal to ", y)
</lang>
- Output:
100000000000000.02 is approximately equal to 100000000000000.02 100.01 is NOT approximately equal to 100.011 1000000000.0000002 is approximately equal to 1000000000.0000001 0.001 is NOT approximately equal to 0.0010000001 1.01e-22 is NOT approximately equal to 0.0 2.0 is approximately equal to 2.0 -2.0 is approximately equal to -2.0 3.141592653589793 is approximately equal to 3.141592653589793
Racket
In Racket, a number literal with decimal point is considered a flonum, an inexact number which could be either 30 or 62 bits depending on machines. By prefixing the literal with #e
, it is now considered an exact, rational number. In this task, we test the approximate equality on both variants:
<lang racket>#lang racket
(define (≈ a b [tolerance 1e-9])
(<= (abs (/ (- a b) (max a b))) tolerance))
(define all-tests
`(([100000000000000.01 100000000000000.011] [100.01 100.011] [,(/ 10000000000000.001 10000.0) 1000000000.0000001000] [0.001 0.0010000001] [0.000000000000000000000101 0.0] [,(* (sqrt 2) (sqrt 2)) 2.0] [,(* (- (sqrt 2)) (sqrt 2)) -2.0] [100000000000000003.0 100000000000000004.0] [3.14159265358979323846 3.14159265358979324]) ([#e100000000000000.01 #e100000000000000.011] [#e100.01 #e100.011] [,(/ #e10000000000000.001 #e10000.0) #e1000000000.0000001000] [#e0.001 #e0.0010000001] [#e0.000000000000000000000101 #e0.0] [,(* (sqrt 2) (sqrt 2)) #e2.0] [,(* (- (sqrt 2)) (sqrt 2)) #e-2.0] [100000000000000003 100000000000000004] [#e3.14159265358979323846 #e3.14159265358979324])))
(define (format-num x)
(~a (~r x #:precision 30) #:min-width 50 #:align 'right))
(for ([tests (in-list all-tests)] [name '("inexact" "exact")])
(printf "~a:\n" name) (for ([test (in-list tests)]) (match-define (list a b) test) (printf "~a ~a: ~a\n" (format-num a) (format-num b) (≈ a b))) (newline))</lang>
- Output:
inexact: 100000000000000.015625000000000000310697263104 100000000000000.015625000000000000310697263104: #t 100.010000000000005116710235406336 100.010999999999995680439855480832: #f 1000000000.000000238418579101562504740864 1000000000.000000119209289550781252370432: #t 0.001000000000000000013287555072 0.001000000100000000093229940736: #f 0.000000000000000000000101 0: #f 2.000000000000000444089209850063 2: #t -2.000000000000000444089209850063 -2: #t 100000000000000000 100000000000000000: #t 3.141592653589793121575456735232 3.141592653589793121575456735232: #t exact: 100000000000000.01 100000000000000.011: #t 100.01 100.011: #f 1000000000.0000001 1000000000.0000001: #t 0.001 0.0010000001: #f 0.000000000000000000000101 0: #f 2.000000000000000444089209850063 2: #t -2.000000000000000444089209850063 -2: #t 100000000000000003 100000000000000004: #t 3.14159265358979323846 3.14159265358979324: #t
Raku
(formerly Perl 6)
Is approximately equal to is a built-in operator in Raku. Unicode ≅, or the ASCII equivalent: =~=. By default it uses a tolerance of 1e-15 times the order of magnitude of the larger comparand, though that is adjustable by setting the dynamic variable $*TOLERANCE to the desired value. Probably a good idea to localize the changed $*TOLERANCE as it will affect all comparisons within its scope.
Most of the following tests are somewhat pointless in Raku. To a large extent, when dealing with Rational values, you don't really need to worry about "approximately equal to", and all of the test values below, with the exception of sqrt(2)
, are Rats by default, and exact. You would have to specifically coerce them to Nums (floating point) to lose the precision.
For example, in Raku, the sum of .1, .2, .3, & .4 is identically equal to 1.
<lang perl6>say 0.1 + 0.2 + 0.3 + 0.4 === 1.0000000000000000000000000000000000000000000000000000000000000000000000000; # True</lang>
It's also approximately equal to 1 but... ¯\_(ツ)_/¯
<lang perl6>for
100000000000000.01, 100000000000000.011, 100.01, 100.011, 10000000000000.001 / 10000.0, 1000000000.0000001000, 0.001, 0.0010000001, 0.000000000000000000000101, 0.0, sqrt(2) * sqrt(2), 2.0, -sqrt(2) * sqrt(2), -2.0, 100000000000000003.0, 100000000000000004.0, 3.14159265358979323846, 3.14159265358979324
-> $a, $b { say "$a ≅ $b: ", $a ≅ $b;
}
say "\nTolerance may be adjusted.";
say 22/7, " ≅ ", π, ": ", 22/7 ≅ π; { # Localize the tolerance to only this block
my $*TOLERANCE = .001; say 22/7, " ≅ ", π, ": ", 22/7 ≅ π;
}</lang>
- Output:
100000000000000.01 ≅ 100000000000000.011: True 100.01 ≅ 100.011: False 1000000000.0000001 ≅ 1000000000.0000001: True 0.001 ≅ 0.0010000001: False 0.000000000000000000000101 ≅ 0: True 2.0000000000000004 ≅ 2: True -2.0000000000000004 ≅ -2: True 100000000000000003 ≅ 100000000000000004: True 3.141592653589793226752 ≅ 3.14159265358979324: True Tolerance may be adjusted. 3.142857 ≅ 3.141592653589793: False 3.142857 ≅ 3.141592653589793: True
REXX
Since the REXX language uses decimal digits (characters) for floating point numbers (and integers), it's just a matter of
choosing the number of decimal digits for the precision to be used for arithmetic (in this case, fifteen decimal digits).
The choosing of the number of decimal digits is performed via the REXX statement: numeric digits nnn <lang rexx>/*REXX program mimics an "approximately equal to" for comparing floating point numbers*/ numeric digits 15 /*what other FP hardware normally uses.*/ @.= /*assign default for the @ array. */ parse arg @.1 /*obtain optional argument from the CL.*/ if @.1= | @.1=="," then do; @.1= 100000000000000.01 100000000000000.011
@.2= 100.01 100.011 @.3= 10000000000000.001 / 10000 1000000000.0000001000 @.4= 0.001 0.0010000001 @.5= 0.00000000000000000000101 0.0 @.6= sqrt(2) * sqrt(2) 2.0 @.7= -sqrt(2) * sqrt(2) '-2.0' @.8= 3.14159265358979323846 3.14159265358979324 /* added ───► */ @.9= 100000000000000003.0 100000000000000004.0 end do j=1 while @.j\== /*process CL argument or the array #s. */ say say center(' processing pair ' j" ",71,'═') /*display a title for the pair of #s. */ parse value @.j with a b /*extract two values from a pair of #s.*/ say 'A=' a /*display the value of A to the term.*/ say 'B=' b /* " " " " B " " " */ say right('A approximately equal to B?', 65) word("false true", 1 + approxEQ(a,b) ) end /*j*/ /* [↑] right─justify text & true/false*/
exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ approxEQ: procedure; parse arg x,y; return x=y /*floating point compare with 15 digits*/ /*──────────────────────────────────────────────────────────────────────────────────────*/ sqrt: procedure; parse arg x; if x=0 then return 0; d=digits(); numeric digits; h=d+6
numeric form; m.=9; parse value format(x,2,1,,0) 'E0' with g "E" _ .; g=g *.5'e'_ %2 do j=0 while h>9; m.j=h; h=h%2+1; end /*j*/ do k=j+5 to 0 by -1; numeric digits m.k; g=(g+x/g)*.5; end /*k*/; return g/1</lang>
- output when using the internal default inputs:
═════════════════════════ processing pair 1 ══════════════════════════ A= 100000000000000.01 B= 100000000000000.011 A approximately equal to B? true ═════════════════════════ processing pair 2 ══════════════════════════ A= 100.01 B= 100.011 A approximately equal to B? false ═════════════════════════ processing pair 3 ══════════════════════════ A= 1000000000 B= 1000000000.0000001000 A approximately equal to B? true ═════════════════════════ processing pair 4 ══════════════════════════ A= 0.001 B= 0.0010000001 A approximately equal to B? false ═════════════════════════ processing pair 5 ══════════════════════════ A= 0.00000000000000000000101 B= 0.0 A approximately equal to B? false ═════════════════════════ processing pair 6 ══════════════════════════ A= 2.00000000000000 B= 2.0 A approximately equal to B? true ═════════════════════════ processing pair 7 ══════════════════════════ A= -2.00000000000000 B= -2.0 A approximately equal to B? true ═════════════════════════ processing pair 8 ══════════════════════════ A= 3.14159265358979323846 B= 3.14159265358979324 A approximately equal to B? true ═════════════════════════ processing pair 9 ══════════════════════════ A= 100000000000000003.0 B= 100000000000000004.0 A approximately equal to B? true
Ruby
Most work went into handling weird Float values like NaN and Infinity. <lang ruby>require "bigdecimal"
testvalues = [[100000000000000.01, 100000000000000.011],
[100.01, 100.011], [10000000000000.001 / 10000.0, 1000000000.0000001000], [0.001, 0.0010000001], [0.000000000000000000000101, 0.0], [(2**0.5) * (2**0.5), 2.0], [-(2**0.5) * (2**0.5), -2.0], [BigDecimal("3.14159265358979323846"), 3.14159265358979324], [Float::NAN, Float::NAN,], [Float::INFINITY, Float::INFINITY], ]
class Numeric
def close_to?(num, tol = Float::EPSILON) return true if self == num return false if (self.to_f.nan? or num.to_f.nan?) # NaN is not even close to itself return false if [self, num].count( Float::INFINITY) == 1 # Infinity is only close to itself return false if [self, num].count(-Float::INFINITY) == 1 (self-num).abs <= tol * ([self.abs, num.abs].max) end
end
testvalues.each do |a,b|
puts "#{a} #{a.close_to?(b) ? '≈' : '≉'} #{b}"
end </lang>
- Output:
100000000000000.02 ≈ 100000000000000.02 100.01 ≉ 100.011 1000000000.0000002 ≈ 1000000000.0000001 0.001 ≉ 0.0010000001 0.101e-21 ≉ 0.0 2.0000000000000004 ≈ 2.0 -2.0000000000000004 ≈ -2.0 0.314159265358979323846e1 ≈ 3.141592653589793 NaN ≉ NaN Infinity ≈ Infinity
Rust
<lang rust>/// Return whether the two numbers `a` and `b` are close. /// Closeness is determined by the `epsilon` parameter - /// the numbers are considered close if the difference between them /// is no more than epsilon * max(abs(a), abs(b)). fn isclose(a: f64, b: f64, epsilon: f64) -> bool {
(a - b).abs() <= a.abs().max(b.abs()) * epsilon
}
fn main() {
fn sqrt(x: f64) -> f64 { x.sqrt() } macro_rules! test { ($a: expr, $b: expr) => { let operator = if isclose($a, $b, 1.0e-9) { '≈' } else { '≉' }; println!("{:>28} {} {}", stringify!($a), operator, stringify!($b)) } }
test!(100000000000000.01, 100000000000000.011); test!(100.01, 100.011); test!(10000000000000.001/10000.0, 1000000000.0000001000); test!(0.001, 0.0010000001); test!(0.000000000000000000000101, 0.0); test!( sqrt(2.0) * sqrt(2.0), 2.0); test!(-sqrt(2.0) * sqrt(2.0), -2.0); test!(3.14159265358979323846, 3.14159265358979324);
}</lang>
- Output:
100000000000000.01 ≈ 100000000000000.011 100.01 ≉ 100.011 10000000000000.001 / 10000.0 ≈ 1000000000.0000001000 0.001 ≉ 0.0010000001 0.000000000000000000000101 ≉ 0.0 sqrt(2.0) * sqrt(2.0) ≈ 2.0 -sqrt(2.0) * sqrt(2.0) ≈ -2.0 3.14159265358979323846 ≈ 3.14159265358979324
Scala
- Output:
Best seen running in your browser by Scastie (remote JVM).
<lang Scala>object Approximate extends App {
val (ok, notOk, ε) = ("👌", "❌", 1e-18)
private def approxEquals(value: Double, other: Double, epsilon: BigDecimal) = scala.math.abs(value - other) < epsilon
private def test(a: BigDecimal, b: BigDecimal, expected: Boolean): Unit = { val result = approxEquals(a.toDouble, b.toDouble, ε) println(f"$a%40.24f ≅ $b%40.24f => $result%5s ${if (expected == result) ok else notOk}") }
test(BigDecimal("100000000000000.010"), BigDecimal("100000000000000.011"), true) test(BigDecimal("100.01"), BigDecimal("100.011"), false) test(BigDecimal(10000000000000.001 / 10000.0), BigDecimal("1000000000.0000001000"), false) test(BigDecimal("0.001"), BigDecimal("0.0010000001"), false) test(BigDecimal("0.000000000000000000000101"), BigDecimal(0), true) test(BigDecimal(math.sqrt(2) * math.sqrt(2d)), BigDecimal(2.0), false) test(BigDecimal(-Math.sqrt(2) * Math.sqrt(2)), BigDecimal(-2.0), false) test(BigDecimal("3.14159265358979323846"), BigDecimal("3.14159265358979324"), true)
}</lang>
Sidef
Two values can be compared for approximate equality by using the built-in operator ≅, available in ASCII as =~=, which does approximate comparison by rounding both operands at (PREC>>2)-1 decimals. However, by default, Sidef uses a floating-point precision of 192 bits. <lang ruby>[
100000000000000.01, 100000000000000.011, 100.01, 100.011, 10000000000000.001 / 10000.0, 1000000000.0000001000, 0.001, 0.0010000001, 0.000000000000000000000101, 0.0, sqrt(2) * sqrt(2), 2.0, -sqrt(2) * sqrt(2), -2.0, sqrt(-2) * sqrt(-2), -2.0, cbrt(3)**3, 3, cbrt(-3)**3, -3, 100000000000000003.0, 100000000000000004.0, 3.14159265358979323846, 3.14159265358979324
].each_slice(2, {|a,b|
say ("#{a} ≅ #{b}: ", a ≅ b)
})</lang>
- Output:
100000000000000.01 ≅ 100000000000000.011: false 100.01 ≅ 100.011: false 1000000000.0000001 ≅ 1000000000.0000001: true 0.001 ≅ 0.0010000001: false 0.000000000000000000000101 ≅ 0: false 2 ≅ 2: true -2 ≅ -2: true -2 ≅ -2: true 3 ≅ 3: true -3-7.82914889268316957969274243345625157631318402415e-58i ≅ -3: true 100000000000000003 ≅ 100000000000000004: false 3.14159265358979323846 ≅ 3.14159265358979324: false
The Number n.round(-k) can be used for rounding the number n to k decimal places. A positive argument can be used for rounding before the decimal point.
<lang ruby>var a = 100000000000000.01 var b = 100000000000000.011
- Rounding at 2 and 3 decimal places, respectively
say (round(a, -2) == round(b, -2)) # true say (round(a, -3) == round(b, -3)) # false</lang>
There is also the built-in approx_cmp(a, b, k) method, which is equivalent with a.round(k) <=> b.round(k).
<lang ruby>var a = 22/7 var b = Num.pi
say ("22/7 ≅ π at 2 decimals: ", approx_cmp(a, b, -2) == 0) say ("22/7 ≅ π at 3 decimals: ", approx_cmp(a, b, -3) == 0)</lang>
- Output:
22/7 ≅ π at 2 decimals: true 22/7 ≅ π at 3 decimals: false
Additionally, the rat_approx method can be used for computing a very good rational approximation to a given real value:
<lang ruby>say (1.33333333.rat_approx == 4/3) # true say (zeta(-5).rat_approx == -1/252) # true</lang>
Rational approximations illustrated for substrings of PI: <lang ruby>for k in (3..19) {
var r = Str(Num.pi).first(k) say ("rat_approx(#{r}) = ", Num(r).rat_approx.as_frac)
}</lang>
- Output:
rat_approx(3.1) = 31/10 rat_approx(3.14) = 22/7 rat_approx(3.141) = 245/78 rat_approx(3.1415) = 333/106 rat_approx(3.14159) = 355/113 rat_approx(3.141592) = 355/113 rat_approx(3.1415926) = 86953/27678 rat_approx(3.14159265) = 102928/32763 rat_approx(3.141592653) = 103993/33102 rat_approx(3.1415926535) = 1354394/431117 rat_approx(3.14159265358) = 833719/265381 rat_approx(3.141592653589) = 17925491/5705861 rat_approx(3.1415926535897) = 126312511/40206521 rat_approx(3.14159265358979) = 144029661/45846065 rat_approx(3.141592653589793) = 325994779/103767361 rat_approx(3.1415926535897932) = 903259831/287516534 rat_approx(3.14159265358979323) = 1726375805/549522486
Swift
Using the solution proposed as an addition to the Swift standard library in SE-0259. Currently this is not accepted, but is likely to be included in the Swift Numerics module.
<lang swift>import Foundation
extension FloatingPoint {
@inlinable public func isAlmostEqual( to other: Self, tolerance: Self = Self.ulpOfOne.squareRoot() ) -> Bool { // tolerances outside of [.ulpOfOne,1) yield well-defined but useless results, // so this is enforced by an assert rathern than a precondition. assert(tolerance >= .ulpOfOne && tolerance < 1, "tolerance should be in [.ulpOfOne, 1).")
// The simple computation below does not necessarily give sensible // results if one of self or other is infinite; we need to rescale // the computation in that case. guard self.isFinite && other.isFinite else { return rescaledAlmostEqual(to: other, tolerance: tolerance) }
// This should eventually be rewritten to use a scaling facility to be // defined on FloatingPoint suitable for hypot and scaled sums, but the // following is good enough to be useful for now. let scale = max(abs(self), abs(other), .leastNormalMagnitude) return abs(self - other) < scale*tolerance }
@usableFromInline internal func rescaledAlmostEqual(to other: Self, tolerance: Self) -> Bool { // NaN is considered to be not approximately equal to anything, not even // itself. if self.isNaN || other.isNaN { return false } if self.isInfinite { if other.isInfinite { return self == other }
// Self is infinite and other is finite. Replace self with the binade // of the greatestFiniteMagnitude, and reduce the exponent of other by // one to compensate. let scaledSelf = Self(sign: self.sign, exponent: Self.greatestFiniteMagnitude.exponent, significand: 1) let scaledOther = Self(sign: .plus, exponent: -1, significand: other)
// Now both values are finite, so re-run the naive comparison. return scaledSelf.isAlmostEqual(to: scaledOther, tolerance: tolerance) }
// If self is finite and other is infinite, flip order and use scaling // defined above, since this relation is symmetric. return other.rescaledAlmostEqual(to: self, tolerance: tolerance) }
}
let testCases = [
(100000000000000.01, 100000000000000.011), (100.01, 100.011), (10000000000000.001 / 10000.0, 1000000000.0000001000), (0.001, 0.0010000001), (0.000000000000000000000101, 0.0), (sqrt(2) * sqrt(2), 2.0), (-sqrt(2) * sqrt(2), -2.0), (3.14159265358979323846, 3.14159265358979324)
]
for testCase in testCases {
print("\(testCase.0), \(testCase.1) => \(testCase.0.isAlmostEqual(to: testCase.1))")
}</lang>
- Output:
100000000000000.02, 100000000000000.02 => true 100.01, 100.011 => false 1000000000.0000002, 1000000000.0000001 => true 0.001, 0.0010000001 => false 1.01e-22, 0.0 => false 2.0000000000000004, 2.0 => true -2.0000000000000004, -2.0 => true 3.141592653589793, 3.141592653589793 => true
Tcl
Using decimal library
Uses tcllib's decimal library. Using a tolerance of 9 significant digits. <lang Tcl>catch {namespace delete test_almost_equal_decimal} ;# Start with a clean namespace
namespace eval test_almost_equal_decimal {
package require Tcl 8.5 ;# required by tcllib package require math::decimal ;# from tcllib namespace import ::math::decimal::* ;# for: setVariable, fromstr, and compare
array set yesno {0 Yes -1 No 1 No} ;# For nice output
# More info here: http://speleotrove.com/decimal/dax3274.html # This puts the library into "simplified" mode. Which # rounds the "decimal digits" in the coefficient to the # number of digits that "precision" is set to. setVariable extended 0 setVariable precision 9
set data { {100000000000000.01 100000000000000.011} {100.01 100.011} {[expr {10000000000000.001 / 10000.0}] 1000000000.0000001000} {0.001 0.0010000001} {0.000000000000000000000101 0.0} {[expr { sqrt(2) * sqrt(2)}] 2.0} {[expr {-sqrt(2) * sqrt(2)}] -2.0} {3.14159265358979323846 3.14159265358979324} } set data [subst $data] ;# resolves expressions in the list
foreach {a b} [join $data] { set a_d [fromstr $a] set b_d [fromstr $b]
puts [format "Is %26s ≈ %21s ? %4s." $a $b $yesno([compare $a_d $b_d])] }
}
</lang>
- Output:
Is 100000000000000.01 ≈ 100000000000000.011 ? Yes. Is 100.01 ≈ 100.011 ? No. Is 1000000000.0000002 ≈ 1000000000.0000001000 ? Yes. Is 0.001 ≈ 0.0010000001 ? No. Is 0.000000000000000000000101 ≈ 0.0 ? No. Is 2.0000000000000004 ≈ 2.0 ? Yes. Is -2.0000000000000004 ≈ -2.0 ? Yes. Is 3.14159265358979323846 ≈ 3.14159265358979324 ? Yes.
Using string manipulation
<lang Tcl>catch {namespace delete test_almost_equal_string} ;# Start with a clean namespace
namespace eval test_almost_equal_string {
package require Tcl 8.4 ;# ?Maybe earlier? array set yesno {1 Yes 0 No} ;# For nice output
proc isClose {a b {prec 9}} { proc toCoeff {n prec} { set repr 40 ;# Chosen to be arbitrarily large to handle most cases set long [format %0.${repr}f $n] ;# Take out of scientific notation set map [string map {. {}} $long] ;# Remove decimal point set trim [string trimleft $map 0] ;# Remove leading zeros # restore string for comparison set len [string length $trim] if {$len < $prec} { set trim "${trim}[string repeat 0 [expr ($prec+1)-$len]]" } # Round last decimal place set rounded [format %0.f "[string range $trim 0 [expr {$prec-1}]].[string index $trim $prec]"] return $rounded } set a_coeff [toCoeff $a $prec] set b_coeff [toCoeff $b $prec]
return [expr {$a_coeff == $b_coeff}] }
set data { {100000000000000.01 100000000000000.011} {100.01 100.011} {[expr {10000000000000.001 / 10000.0}] 1000000000.0000001000} {0.001 0.0010000001} {0.000000000000000000000101 0.0} {[expr { sqrt(2) * sqrt(2)}] 2.0} {[expr {-sqrt(2) * sqrt(2)}] -2.0} {3.14159265358979323846 3.14159265358979324} } set data [subst $data] ;# resolves expressions in the list
foreach {a b} [join $data] { puts [format "Is %26s ≈ %21s ? %4s." $a $b $yesno([isClose $a $b])] }
} </lang>
- Output:
Is 100000000000000.01 ≈ 100000000000000.011 ? Yes. Is 100.01 ≈ 100.011 ? No. Is 1000000000.0000002 ≈ 1000000000.0000001000 ? Yes. Is 0.001 ≈ 0.0010000001 ? No. Is 0.000000000000000000000101 ≈ 0.0 ? No. Is 2.0000000000000004 ≈ 2.0 ? Yes. Is -2.0000000000000004 ≈ -2.0 ? Yes. Is 3.14159265358979323846 ≈ 3.14159265358979324 ? Yes.
Wren
<lang ecmascript>var tol = 1e-16 var pairs = [
[100000000000000.01, 100000000000000.011], [100.01, 100.011], [10000000000000.001 / 10000.0, 1000000000.0000001000], [0.001, 0.0010000001], [0.000000000000000000000101, 0.0], [2.sqrt * 2.sqrt, 2.0], [-2.sqrt * 2.sqrt, -2.0], [3.14159265358979323846, 3.14159265358979324]
] System.print("Approximate equality of test cases for a tolerance of %(tol):") var i = 0 for (pair in pairs) {
i = i + 1 System.print(" %(i) -> %((pair[0] - pair[1]).abs < tol)")
}</lang>
- Output:
Approximate equality of test cases for a tolerance of 1e-16: 1 -> true 2 -> false 3 -> false 4 -> false 5 -> true 6 -> false 7 -> false 8 -> true
zkl
Floats are 64 bit and have the closeTo method, which takes a comparison value and tolerance. If the tolerance is >=0, comparison is absolute. If tolerance is <0 (and x!=0 and y!=0), the comparison is relative. <lang zkl>testValues:=T(
T(100000000000000.01,100000000000000.011), T(100.01, 100.011), T(10000000000000.001 / 10000.0, 1000000000.0000001), T(0.001, 0.0010000001), T(0.00000000000000000101, 0.0), T( (2.0).sqrt()*(2.0).sqrt(), 2.0), T( -(2.0).sqrt()*(2.0).sqrt(), -2.0), T(100000000000000003.0, 100000000000000004.0), T(3.14159265358979323846, 3.14159265358979324)
);
tolerance:=-1e-9; // <0 for relative comparison foreach x,y in (testValues){
maybeNot:=( if(x.closeTo(y,tolerance)) " \u2248" else "!\u2248" ); println("% 25.19g %s %- 25.19g %g".fmt(x,maybeNot,y, (x-y).abs()));
}</lang>
- Output:
100000000000000.0156 ≈ 100000000000000.0156 0 100.0100000000000051 !≈ 100.0109999999999957 0.001 1000000000.000000238 ≈ 1000000000.000000119 1.19209e-07 0.001000000000000000021 !≈ 0.001000000100000000055 1e-10 1.010000000000000018e-18 !≈ 0 1.01e-18 2.000000000000000444 ≈ 2 4.44089e-16 -2.000000000000000444 ≈ -2 4.44089e-16 100000000000000000 ≈ 100000000000000000 0 3.141592653589793116 ≈ 3.141592653589793116 0