Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions circle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (
"github.com/go-spatial/geom/cmp"
)

const tolerance = geom.TOLERANCE
const tolerance = geom.Tolerance
const bitTolerance = geom.BitTolerance

func TestCircleFromPoints(t *testing.T) {
type tcase struct {
Expand All @@ -28,17 +29,17 @@ func TestCircleFromPoints(t *testing.T) {
return
}

if !cmp.Float64(circle.Radius, tc.circle.Radius, tolerance) {
if !cmp.Float64(circle.Radius, tc.circle.Radius, tolerance, bitTolerance) {
t.Errorf("circle radius, expected %v got %v", tc.circle, circle)
return
}

if !cmp.Float64(circle.Center[0], tc.circle.Center[0], tolerance) {
if !cmp.Float64(circle.Center[0], tc.circle.Center[0], tolerance, bitTolerance) {
t.Errorf("circle x, expected %v got %v", tc.circle, circle)
return
}

if !cmp.Float64(circle.Center[1], tc.circle.Center[1], tolerance) {
if !cmp.Float64(circle.Center[1], tc.circle.Center[1], tolerance, bitTolerance) {
t.Errorf("circle y, expected %v got %v", tc.circle, circle)
return
}
Expand Down
45 changes: 28 additions & 17 deletions cmp/cmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ import (
"github.com/go-spatial/geom"
)

// TOLERANCE is the epsilon value used in comparing floats.
const TOLERANCE = 0.000001
// Tolerance is the epsilon value used in comparing floats with zero
const Tolerance = 0.000001

// BitTolerance is the epsilon value for comaparing float bit-patterns.
// It was calculated as math.Float64bits(1.000001) - math.Float64bits(1.0)
const BitTolerance = 4503599627

var (
NilPoint = (*geom.Point)(nil)
Expand All @@ -21,10 +25,10 @@ var (
)

// FloatSlice compare two sets of float slices.
func FloatSlice(f1, f2 []float64) bool { return Float64Slice(f1, f2, TOLERANCE) }
func FloatSlice(f1, f2 []float64) bool { return Float64Slice(f1, f2, Tolerance, BitTolerance) }

// Float64Slice compares two sets of float64 slices within the given tolerance.
func Float64Slice(f1, f2 []float64, tolerance float64) bool {
func Float64Slice(f1, f2 []float64, tolerance float64, bitTolerance int64) bool {
if len(f1) != len(f2) {
return false
}
Expand All @@ -38,32 +42,39 @@ func Float64Slice(f1, f2 []float64, tolerance float64) bool {
sort.Float64s(f1s)
sort.Float64s(f2s)
for i := range f1s {
if !Float64(f1s[i], f2s[i], tolerance) {
if !Float64(f1s[i], f2s[i], tolerance, bitTolerance) {
return false
}
}
return true
}

// Float64 compares two floats to see if they are within the given tolerance.
func Float64(f1, f2, tolerance float64) bool {
if math.IsInf(f1, 1) {
return math.IsInf(f2, 1)
}
if math.IsInf(f2, 1) {
return math.IsInf(f1, 1)
func Float64(f1, f2, tolerance float64, bitTolerance int64) bool {

// handle infinity
if math.IsInf(f1, 0) || math.IsInf(f2, 0) {
return math.IsInf(f1, -1) == math.IsInf(f2, -1) &&
math.IsInf(f1, 1) == math.IsInf(f2, 1)
}
if math.IsInf(f1, -1) {
return math.IsInf(f2, -1)

// -0.0 exist but -0.0 == 0.0 is true
if f1 == 0 || f2 == 0 {
return math.Abs(f2 - f1) < tolerance
}
if math.IsInf(f2, -1) {
return math.IsInf(f1, -1)

i1 := int64(math.Float64bits(f1))
i2 := int64(math.Float64bits(f2))
d := i2 - i1

if d < 0 {
return d > -bitTolerance
}
return math.Abs(f1-f2) < tolerance
return d < bitTolerance
}

// Float compares two floats to see if they are within 0.00001 from each other. This is the best way to compare floats.
func Float(f1, f2 float64) bool { return Float64(f1, f2, TOLERANCE) }
func Float(f1, f2 float64) bool { return Float64(f1, f2, Tolerance, BitTolerance) }

// Extent will check to see if the Extents's are the same.
func Extent(extent1, extent2 [4]float64) bool {
Expand Down
65 changes: 62 additions & 3 deletions cmp/cmp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import (
"math"
"testing"

geom "github.com/go-spatial/geom"
"github.com/go-spatial/geom"
gtesting "github.com/go-spatial/geom/testing"
)

/*
Expand Down Expand Up @@ -917,27 +918,65 @@ func TestFloat64(t *testing.T) {
type tcase struct {
f1, f2 float64
t float64
b int64
e bool
}

// bit tolerence for 2 significant digits
bitTolerance2 := int64(math.Float64bits(1.1) - math.Float64bits(1))
// float representation of negative zero
negativeZero := math.Float64frombits(1 << 63)

fn := func(t *testing.T, tc tcase) {
g := Float64(tc.f1, tc.f2, tc.t)
g := Float64(tc.f1, tc.f2, tc.t, tc.b)
if g != tc.e {
t.Errorf(" Float64, expected %v, got %v", tc.e, g)
}
}

tests := map[string]tcase{
"t simple .01 ": {
"t simple .01": {
f1: 0.11,
f2: 0.111,
t: 0.01,
b: bitTolerance2,
e: true,
},
"f simple .01": {
f1: 0.11,
f2: 0.121,
t: 0.01,
b: bitTolerance2,
e: false,
},
"t 0 .01": {
f1: 0.0,
f2: 0.001,
t: 0.01,
b: bitTolerance2,
e: true,
},
"f 0 .01": {
f1: 0.0,
f2: 0.02,
t: 0.01,
b: bitTolerance2,
e: false,
},
"t 0 0": {
f1: 0.0,
f2: 0.0,
t: 0.01,
b: bitTolerance2,
e: true,
},
"t 0 -0": {
f1: 0.0,
f2: negativeZero,
t: 0.01,
b: bitTolerance2,
e: true,
},
"t inf 1 0": {
f1: math.Inf(1),
f2: math.Inf(1),
Expand Down Expand Up @@ -984,3 +1023,23 @@ func TestFloat64(t *testing.T) {
t.Run(name, func(t *testing.T) { fn(t, tc) })
}
}

func BenchmarkCmpFloat(b *testing.B) {

sin := gtesting.SinLineString(10, 0, 100, 1000)
var result bool

for i := 0; i < b.N; i++ {
for _, v := range sin {
for _, vv := range sin {
// xor
result = result != Float(v[0], vv[0])
}
}
}

// use the result
if result {
print("")
}
}
3 changes: 2 additions & 1 deletion geom.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Package geom describes geometry interfaces.
package geom

const TOLERANCE = 0.000001
const Tolerance = 0.000001
const BitTolerance = 4503599627

// Geometry is an object with a spatial reference.
// if a method accepts a Geometry type it's only expected to support the geom types in this package
Expand Down
4 changes: 3 additions & 1 deletion slippy/tile_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package slippy_test

import (
"math"
"strconv"
"testing"

Expand Down Expand Up @@ -41,8 +42,9 @@ func TestNewTile(t *testing.T) {
}
{
bounds := tile.Extent4326()
bitTolerence2 := int64(math.Float64bits(1.01) - math.Float64bits(1.00))
for i := 0; i < 4; i++ {
if !cmp.Float64(bounds[i], tc.eBounds[i], 0.01) {
if !cmp.Float64(bounds[i], tc.eBounds[i], 0.01, bitTolerence2) {
t.Errorf("bounds[%v] , expected %v got %v", i, tc.eBounds[i], bounds[i])

}
Expand Down