Reputation: 1396
I tried to compare two identical latitude, which is of type Double
, and when I print the result, it's evaluated as false
.
print("spot latitude: " + String(spot.location.latitude))
print("first object: " + String(firstSpot.location.latitude))
print(spot.location.latitude == firstSpot.location.latitude)
Output:
spot latitude: 32.8842183049047
first object: 32.8842183049047
false
Anyone has any idea what's going on?
Upvotes: 25
Views: 24666
Reputation: 8745
Merging different Swift 4
technics together:
import Foundation
infix operator ~==
infix operator ~<=
infix operator ~>=
extension Double {
static func ~== (lhs: Double, rhs: Double) -> Bool {
// If we add even a single zero more,
// our Decimal test would fail.
fabs(lhs - rhs) < 0.000000000001
}
static func ~<= (lhs: Double, rhs: Double) -> Bool {
(lhs < rhs) || (lhs ~== rhs)
}
static func ~>= (lhs: Double, rhs: Double) -> Bool {
(lhs > rhs) || (lhs ~== rhs)
}
}
// Check if two double variables are almost equal:
if a ~== b {
print("was nearly equal!")
}
Note that
~<=
is the fuzzy-compare version of less-than (<=
) operator,
And~>=
is the fuzzy-compare greater-than (>=
) operator.Also, originally I was using
Double.ulpOfOne
, but changed to a constant (to be even more fuzzy).Finally, as mentioned on my profile, usage under
Apache 2.0
license is allowed as well (without attribution need).
import Foundation
import XCTest
@testable import MyApp
class MathTest: XCTestCase {
func testFuzzyCompare_isConstantBigEnough() {
// Dummy.
let decimal: Decimal = 3062.36
let double: Double = 3062.36
// With casting up from low-precession type.
let doubleCasted: Double = NSDecimalNumber(decimal: decimal).doubleValue
// Actual test.
XCTAssertEqual(decimal, Decimal(doubleCasted));
XCTAssertNotEqual(double, doubleCasted);
XCTAssertTrue(double ~== doubleCasted);
}
func testDouble_correctConstant() {
XCTAssertEqual(Double.ulpOfOne, 2.220446049250313e-16)
}
}
Upvotes: 3
Reputation: 26385
Comparing equality in doubles rarely gave you the expected answer this is due to how doubles are stored. You can create custom operators, keeping in mind that you should work with sort of accuracy.
To know more you can check this answer, even if it speaks about ObjC the principles are super valid.
Since I had the same problem checking online I've found this answer on the apple dev forum.
This function should do the trick, you can easily create a custom operator:
func doubleEqual(_ a: Double, _ b: Double) -> Bool {
return fabs(a - b) < Double.ulpOfOne
}
I've tried to convert from swift 2.x to 3.x it seems that the macro DBL_EPSILON
is not available anymore.
Upvotes: 36
Reputation: 116
One-liner with flexible precision. Works well for money
extension Float {
func compare(with number: Float, precision: Int = 2) -> Bool {
abs(self.distance(to: number)) < powf(10, Float(-precision))
}
}
Example
let float1: Float = 0.1
let float2: Float = 0.2
(float1 * float2).compare(with: 0.02) // TRUE
(float1 * float2).compare(with: 0.02, precision: 1) // TRUE
(float1 * float2).compare(with: 0.02, precision: 3) // TRUE
Float(0.021).compare(with: 0.02) // TRUE
Float(0.021).compare(with: 0.02, precision: 3) // FALSE
(float1 * float2).isEqual(to: 0.02) // FALSE
Upvotes: 1
Reputation: 432
Please follow the below-updated code
var a = -21.5
var b = 305.15
if(a.isEqual(to: b)){
print(a)
} else {
print(b)
}
Output
//prints 305.15
Upvotes: -3
Reputation: 1210
You can use this extension
extension FloatingPoint {
func isNearlyEqual(to value: Self) -> Bool {
return abs(self - value) <= .ulpOfOne
}
}
or according to this guide
extension FloatingPoint {
func isNearlyEqual(to value: Self) -> Bool {
let absA = abs(self)
let absB = abs(value);
let diff = abs(self - value);
if self == value { // shortcut, handles infinities
return true
} else if self == .zero || value == .zero || (absA + absB) < Self.leastNormalMagnitude {
// a or b is zero or both are extremely close to it
// relative error is less meaningful here
return diff < Self.ulpOfOne * Self.leastNormalMagnitude
} else { // use relative error
return diff / min((absA + absB), Self.greatestFiniteMagnitude) < .ulpOfOne;
}
}
}
Upvotes: 7
Reputation: 12677
Probably solution with rounding is best choice to get close values:
extension Double {
static func equal(_ lhs: Double, _ rhs: Double, precise value: Int? = nil) -> Bool {
guard let value = value else {
return lhs == rhs
}
return lhs.precised(value) == rhs.precised(value)
}
func precised(_ value: Int = 1) -> Double {
let offset = pow(10, Double(value))
return (self * offset).rounded() / offset
}
}
// values retrieving
a: 64.3465535142464, b: 64.3465535142464
// values debug description
a: 64.346553514246409, b: 64.346553514246395
// calculations
a == b // false
a.precised(10) == b.precised(10) // true
// or
Double.equal(a, b) // false
Double.equal(a, b, precise: 10) // true
If using amendment with epsilon, anyway I get false equal with doubles:
// values retrieving
a: 64.3465535142464, b: 64.3465535142464
// values debug description
a: 64.346553514246409, b: 64.346553514246395
// calculations
a == b // false
a - b // 1.4210854715202e-14
a - b < .ulpOfOne // false
Upvotes: 8
Reputation: 7900
I found a solution, that works pretty well for me. It is flexible, because you can define the "precision" for the comparing by passing the number of fractional digits that should be regarded and made it an extension of Double.
Lets say we have two values for example:
These values are considerd "equal" when only 3 fractional digits are regarded, but regarded es "not equal" regarding 8 fractional digits.
extension Double {
/// Compares the receiver (Double) with annother Double considering a defined
/// number of fractional digits.
func checkIsEqual(toDouble pDouble : Double, includingNumberOfFractionalDigits : Int) -> Bool {
let denominator : Double = pow(10.0, Double(includingNumberOfFractionalDigits))
let maximumDifference : Double = 1.0 / denominator
let realDifference : Double = fabs(self - pDouble)
if realDifference >= maximumDifference {
return false
} else {
return true
}
}
}
Upvotes: 1
Reputation: 1350
Comparing double or float values with == will not give the expected result in most programming languages, which means that numbers that you think should be equal are in fact slightly different. Instead compute the absolute difference and handle the numbers as equal if the difference is below some threshold. See Compare double to zero using epsilon for more explanations.
Upvotes: 10
Reputation: 804
I think because your two Double numbers are actually different. When you convert Double to String in decimal number, rounding can't be avoided due to floating point representation. Even if two Strings look the same, actual numbers can differ. See this for more detail.
You can examine two numbers further by specifying more digits with:
print(String(format: "%.20f", double_number))
Upvotes: 4