比较两个相同的Double值返回false Swift 3

pkbketx9  于 2022-10-31  发布在  Swift
关注(0)|答案(8)|浏览(155)

我尝试比较两个相同的纬度,其类型为Double,当我打印结果时,它的计算结果为false

print("spot latitude: " + String(spot.location.latitude))
print("first object: " + String(firstSpot.location.latitude))  
print(spot.location.latitude == firstSpot.location.latitude)

输出量:

spot latitude: 32.8842183049047
first object: 32.8842183049047
false

有人知道发生了什么吗?

7vux5j2d

7vux5j2d1#

比较双精度数中的相等性很难得到预期的结果,这是由于双精度数的存储方式所致。您可以创建自定义运算符,请记住,您应该以一定的准确性进行操作。
要了解更多信息,您可以查看此answer,即使它讲述的是ObjC,这些原则也是非常有效的。
由于我在网上也遇到了同样的问题,我在苹果开发论坛上找到了这个answer
这个函数应该够用了,你可以很容易地创建一个自定义的运算符:

func doubleEqual(_ a: Double, _ b: Double) -> Bool {
    return fabs(a - b) < Double.ulpOfOne
}

我试着从swift 2.x转换到3.x,似乎宏DBL_EPSILON不再可用。

xlpyo6sf

xlpyo6sf2#

在大多数编程语言中,用==比较双精度型或浮点型值不会给予预期的结果,这意味着你认为应该相等的数字实际上略有不同。相反,计算绝对差值,如果差值低于某个阈值,则将这些数字视为相等。有关更多解释,请参阅Compare double to zero using epsilon

zaqlnxep

zaqlnxep3#

雨燕5,4
可能四舍五入的解决方案是获得接近值的最佳选择:

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

如果用epsilon来修正,反正我得到的假等于是双精度的:

// 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
iyfamqjs

iyfamqjs4#

您可以使用此扩展名

extension FloatingPoint {
    func isNearlyEqual(to value: Self) -> Bool {
        return abs(self - value) <= .ulpOfOne
    }
}

或根据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;
        }
    }
}
mqkwyuun

mqkwyuun5#

我想是因为你的两个双精度数实际上是不同的。当你把双精度数转换成十进制数的字符串时,由于浮点数的表示,四舍五入是不可避免的。即使两个字符串看起来一样,实际的数字也可能不同。更多细节请看这个。
您可以使用指定更多数字来进一步检查两个数字:

print(String(format: "%.20f", double_number))
vdgimpew

vdgimpew6#

斯威夫特4.1:

我找到了一个解决方案,非常适合我。它灵活,因为您可以定义比较的“精度”,方法是传递应该考虑的小数位数,并将其作为Double的扩展。
例如,假设我们有两个值:

  • 值01 = 5.001且
  • 数值02 = 5.00100004

当仅考虑3个小数位时,这些值被视为“相等”,但当考虑8个小数位时,这些值被视为“不相等”。

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
        }
   }
}
krugob8w

krugob8w7#

将不同的Swift 4技术融合在一起:

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!")
}

注意~<=是小于(<=)运算符的模糊比较版本,

~>=是模糊比较大于(>=)运算符。
此外,最初我使用的是Double.ulpOfOne,但改为常量(更模糊)。
最后,正如我的个人资料中提到的,在Apache 2.0许可证下的使用也是允许的(没有归属需要)。
测试

import Foundation
import XCTest
@testable import MyApp

class MathTest: XCTestCase {
    func testFuzzyCompare_isConstantBigEnough() {
        // Dummy.
        let decimal: Decimal = 3062.36
        let double: Double = NSDecimalNumber(decimal: decimal).doubleValue
        let doubleCorrect: Double = 3062.36

        // Actual test.
        XCTAssertEqual(decimal, Decimal(double));
        XCTAssertNotEqual(doubleCorrect, double);
        XCTAssertTrue(doubleCorrect ~== double);
    }

    func testDouble_correctConstant() {
        XCTAssertEqual(Double.ulpOfOne, 2.220446049250313e-16)
    }
}
e4yzc0pl

e4yzc0pl8#

请遵循以下更新的代码

var a = -21.5
    var b = 305.15

   if(a.isEqual(to: b)){
     print(a)
   } else {
     print(b)
   }

输出量

//prints 305.15

相关问题