Upgrade to Pro — share decks privately, control downloads, hide ads and more …

浮動小数の比較について

 浮動小数の比較について

XCTestのXCTAssertEqual(_:_:accuracy:_:file:line:)とSwift Numericsが提供するisApproximatelyEqual()の違いについて

Avatar for Kishikawa Katsumi

Kishikawa Katsumi

February 12, 2026
Tweet

More Decks by Kishikawa Katsumi

Other Decks in Programming

Transcript

  1. let center = CGPoint(x: 900, y: 900) let radius: CGFloat

    = 1000 let p = pointOnCircle(center: center, radius: radius, angle: .pi / 2) #expect(p.x == center.x)
  2. let center = CGPoint(x: 900, y: 900) let radius: CGFloat

    = 1000 let p = pointOnCircle(center: center, radius: radius, angle: .pi / 2) XCTAssertEqual(p.x, center.x, accuracy: 0.000001)
  3. XCTest Swift Testing XCTAssert(x), XCTAssertTrue(x) #expect(x) XCTAssertFalse(x) #expect(!x) XCTAssertNil(x) #expect(x

    == nil) XCTAssertNotNil(x) #expect(x != nil) XCTAssertEqual(x, y) #expect(x == y) XCTAssertNotEqual(x, y) #expect(x != y) XCTAssertIdentical(x, y) #expect(x === y) XCTAssertNotIdentical(x, y) #expect(x !== y) XCTAssertGreaterThan(x, y) #expect(x > y) XCTAssertGreaterThanOrEqual(x, y) #expect(x >= y) XCTAssertLessThanOrEqual(x, y) #expect(x <= y) XCTAssertLessThan(x, y) #expect(x < y) XCTAssertThrowsError(try f()) #expect(throws: (any Error).self) { try f() } XCTAssertThrowsError(try f()) { error in … } let error = #expect(throws: (any Error).self) { try f() } XCTAssertNoThrow(try f()) #expect(throws: Never.self) { try f() } try XCTUnwrap(x) try #require(x) XCTFail("…") Issue.record("…")
  4. 9$5FTU4XJGU5FTUJOH The testing library doesn’t provide an equivalent of XCTAssertEqual(_:_:accuracy:_:file:line:).

    To compare two numeric values within a speci fi ed accuracy, use isApproximatelyEqual() from swift-numerics.
  5. 9$"TTFSU&RVBM *NQMFNFOUBUJPO private func areEqual<T: Numeric>(_ exp1: T, _ exp2:

    T, accuracy: T) -> Bool { // Test with equality first to handle comparing inf/-inf with itself. if exp1 == exp2 { return true } else { // NaN values are handled implicitly, since the <= operator returns false when comparing any value to NaN. let difference = (exp1.magnitude > exp2.magnitude) ? exp1 - exp2 : exp2 - exp1 return difference.magnitude <= accuracy.magnitude } } https://github.com/swiftlang/swift-corelibs-foundation/blob/main/Sources/XCTest/Public/XCTAssert.swift
  6. 9$"TTFSU&RVBM *NQMFNFOUBUJPO private func areEqual<T: Numeric>(_ exp1: T, _ exp2:

    T, accuracy: T) -> Bool { // Test with equality first to handle comparing inf/-inf with itself. if exp1 == exp2 { return true } else { // NaN values are handled implicitly, since the <= operator returns false when comparing any value to NaN. let difference = (exp1.magnitude > exp2.magnitude) ? exp1 - exp2 : exp2 - exp1 return difference.magnitude <= accuracy.magnitude } } https://github.com/swiftlang/swift-corelibs-foundation/blob/main/Sources/XCTest/Public/XCTAssert.swift
  7. 9$"TTFSU&RVBM *NQMFNFOUBUJPO private func areEqual<T: Numeric>(_ exp1: T, _ exp2:

    T, accuracy: T) -> Bool { // Test with equality first to handle comparing inf/-inf with itself. if exp1 == exp2 { return true } else { // NaN values are handled implicitly, since the <= operator returns false when comparing any value to NaN. let difference = (exp1.magnitude > exp2.magnitude) ? exp1 - exp2 : exp2 - exp1 return difference.magnitude <= accuracy.magnitude } } https://github.com/swiftlang/swift-corelibs-foundation/blob/main/Sources/XCTest/Public/XCTAssert.swift
  8. 9$"TTFSU&RVBM *NQMFNFOUBUJPO private func areEqual<T: Numeric>(_ exp1: T, _ exp2:

    T, accuracy: T) -> Bool { // Test with equality first to handle comparing inf/-inf with itself. if exp1 == exp2 { return true } else { // NaN values are handled implicitly, since the <= operator returns false when comparing any value to NaN. let difference = (exp1.magnitude > exp2.magnitude) ? exp1 - exp2 : exp2 - exp1 return difference.magnitude <= accuracy.magnitude } } https://github.com/swiftlang/swift-corelibs-foundation/blob/main/Sources/XCTest/Public/XCTAssert.swift
  9. https://github.com/apple/swift-numerics/blob/main/Sources/RealModule/ApproximateEquality.swift public func isApproximatelyEqual<Magnitude>( to other: Self, absoluteTolerance: Magnitude, relativeTolerance:

    Magnitude = 0, norm: (Self) -> Magnitude ) -> Bool where Magnitude: FloatingPoint { assert( absoluteTolerance >= 0 && absoluteTolerance.isFinite, "absoluteTolerance should be non-negative and finite, " + "but is \(absoluteTolerance)." ) assert( relativeTolerance >= 0 && relativeTolerance <= 1, "relativeTolerance should be non-negative and <= 1, " + "but is \(relativeTolerance)." ) if self == other { return true } let delta = norm(self - other) let scale = max(norm(self), norm(other)) let bound = max(absoluteTolerance, scale*relativeTolerance) return delta.isFinite && delta <= bound }
  10. r ઈରޡࠩabs(a - b) < tolerance ʢྫʣ9$5FTUͷXCTAssertEqual(_:_:accuracy:) r ઈରޡࠩ ૬ରޡࠩabs(a

    - b) <= max(absTol, scale * relTol) ʢྫʣTXJGUOVNFSJDT r ओʹ૬ରޡࠩɺθϩ෇ۙΛಛผѻ͍ ʢྫʣ4& ઈରޡࠩɾ૬ରޡࠩ 'MPBUJOH1PJOU$PNQBSJTPO
  11. r θϩ෇ۙͷখ͞ͳ஋͸ઈରޡࠩɺڊେͳ஋ʹରͯ͠͸૬ରޡࠩΛ࢖͏ r ͲͷΑ͏ͳ஋Ͱ΋ൺֱΛ౷Ұతͳํ๏Ͱߦ͑Δ r ୯ʹઈରޡࠩͱ૬ରޡࠩΛ྆ํࢦఆ͍ͯ͠ΔΘ͚Ͱ͸ͳ͘ɺҰͭͷࣜʹΑͬͯ ∣a−b∣ ≤ max(absTol, scale

    × relTol) ઈରޡࠩʢBCT5PMʣͱ૬ରޡࠩʢTDBMFºSFM5PMʣ͕౳͘͠ͳΔڥքͰࣗಈతʹ ੾ΓସΘΔ ઈରޡࠩͱ૬ରޡࠩͷ߹੒ʢ4XJGU/VNFSJDTʣ ઈରޡࠩɾ૬ରޡࠩ
  12. https://github.com/apple/swift-numerics/blob/main/Sources/RealModule/ApproximateEquality.swift public func isApproximatelyEqual<Magnitude>( to other: Self, absoluteTolerance: Magnitude, relativeTolerance:

    Magnitude = 0, norm: (Self) -> Magnitude ) -> Bool where Magnitude: FloatingPoint { assert( absoluteTolerance >= 0 && absoluteTolerance.isFinite, "absoluteTolerance should be non-negative and finite, " + "but is \(absoluteTolerance)." ) assert( relativeTolerance >= 0 && relativeTolerance <= 1, "relativeTolerance should be non-negative and <= 1, " + "but is \(relativeTolerance)." ) if self == other { return true } let delta = norm(self - other) let scale = max(norm(self), norm(other)) let bound = max(absoluteTolerance, scale*relativeTolerance) return delta.isFinite && delta <= bound }
  13. https://github.com/apple/swift-numerics/blob/main/Sources/RealModule/ApproximateEquality.swift public func isApproximatelyEqual<Magnitude>( to other: Self, absoluteTolerance: Magnitude, relativeTolerance:

    Magnitude = 0, norm: (Self) -> Magnitude ) -> Bool where Magnitude: FloatingPoint { assert( absoluteTolerance >= 0 && absoluteTolerance.isFinite, "absoluteTolerance should be non-negative and finite, " + "but is \(absoluteTolerance)." ) assert( relativeTolerance >= 0 && relativeTolerance <= 1, "relativeTolerance should be non-negative and <= 1, " + "but is \(relativeTolerance)." ) if self == other { return true } let delta = norm(self - other) let scale = max(norm(self), norm(other)) let bound = max(absoluteTolerance, scale*relativeTolerance) return delta.isFinite && delta <= bound }
  14. https://github.com/apple/swift-numerics/blob/main/Sources/RealModule/ApproximateEquality.swift public func isApproximatelyEqual<Magnitude>( to other: Self, absoluteTolerance: Magnitude, relativeTolerance:

    Magnitude = 0, norm: (Self) -> Magnitude ) -> Bool where Magnitude: FloatingPoint { assert( absoluteTolerance >= 0 && absoluteTolerance.isFinite, "absoluteTolerance should be non-negative and finite, " + "but is \(absoluteTolerance)." ) assert( relativeTolerance >= 0 && relativeTolerance <= 1, "relativeTolerance should be non-negative and <= 1, " + "but is \(relativeTolerance)." ) if self == other { return true } let delta = norm(self - other) let scale = max(norm(self), norm(other)) let bound = max(absoluteTolerance, scale*relativeTolerance) return delta.isFinite && delta <= bound }
  15. https://github.com/apple/swift-numerics/blob/main/Sources/RealModule/ApproximateEquality.swift public func isApproximatelyEqual<Magnitude>( to other: Self, absoluteTolerance: Magnitude, relativeTolerance:

    Magnitude = 0, norm: (Self) -> Magnitude ) -> Bool where Magnitude: FloatingPoint { assert( absoluteTolerance >= 0 && absoluteTolerance.isFinite, "absoluteTolerance should be non-negative and finite, " + "but is \(absoluteTolerance)." ) assert( relativeTolerance >= 0 && relativeTolerance <= 1, "relativeTolerance should be non-negative and <= 1, " + "but is \(relativeTolerance)." ) if self == other { return true } let delta = norm(self - other) let scale = max(norm(self), norm(other)) let bound = max(absoluteTolerance, scale*relativeTolerance) return delta.isFinite && delta <= bound }
  16. r %PDVNFOUUIFSFDPNNFOEFEQBUUFSOGPSQFSGPSNJOH fl PBUJOHQPJOU FRVBMJUZXJUIBDDVSBDZFYQFDUBUJPOT BOBMPHPVTUP 9$5"TTFSU&RVBM @@BDDVSBDZ  IUUQTHJUIVCDPNTXJGUMBOHTXJGUUFTUJOHJTTVFT

    r 9$5"TTFSU&RVBMXJUIBDDVSBDZ IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOYDUFTU YDUBTTFSUFRVBM @@BDDVSBDZ@ fi MFMJOF FQV r "QQSPYJNBUF&RVBMJUZTXJGU 4XJGU/VNFSJDT  IUUQTHJUIVCDPNBQQMFTXJGUOVNFSJDTCMPCNBJO4PVSDFT3FBM.PEVMF "QQSPYJNBUF&RVBMJUZTXJGU ࢀߟจݙ