├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── FloatingPointApproximation.playground ├── Contents.swift └── contents.xcplayground ├── FloatingPointApproximation.xcodeproj ├── FloatingPointApproximationTests_Info.plist ├── FloatingPointApproximation_Info.plist ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ ├── FloatingPointApproximation.xcscheme │ └── xcschememanagement.plist ├── FloatingPointApproximation.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── LICENSE.md ├── Package.swift ├── README.md ├── Sources └── FloatingPointApproximation │ └── FloatingPoint+Approximation.swift └── Tests ├── FloatingPointApproximationTests ├── FloatingPointApproximationTests.swift └── XCTestManifests.swift └── LinuxMain.swift /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [mattt] 2 | custom: https://flight.school/books/numbers 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | xcode_project: FloatingPointApproximation.xcodeproj 3 | xcode_scheme: FloatingPointApproximation 4 | xcode_destination: platform=macOs 5 | -------------------------------------------------------------------------------- /FloatingPointApproximation.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | import FloatingPointApproximation 2 | 3 | 0.1 + 0.2 == 0.3 4 | 0.1 + 0.2 ==~ 0.3 5 | 6 | (0.1 + 0.2).isApproximatelyEqual(to: 0.3, within: .ulpOfOne) 7 | 8 | 1e-20 + 2e-20 == 3e-20 9 | 10 | Double.ulpOfOne 11 | 12 | 13 | let actual = 1e25 + 2e25 14 | let expected = 3e25 15 | abs(expected - actual) < .ulpOfOne // false 16 | -------------------------------------------------------------------------------- /FloatingPointApproximation.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /FloatingPointApproximation.xcodeproj/FloatingPointApproximationTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /FloatingPointApproximation.xcodeproj/FloatingPointApproximation_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /FloatingPointApproximation.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = "1"; 4 | objectVersion = "46"; 5 | objects = { 6 | "FloatingPointApproximation::FloatingPointApproximation" = { 7 | isa = "PBXNativeTarget"; 8 | buildConfigurationList = "OBJ_19"; 9 | buildPhases = ( 10 | "OBJ_22", 11 | "OBJ_24" 12 | ); 13 | dependencies = ( 14 | ); 15 | name = "FloatingPointApproximation"; 16 | productName = "FloatingPointApproximation"; 17 | productReference = "FloatingPointApproximation::FloatingPointApproximation::Product"; 18 | productType = "com.apple.product-type.framework"; 19 | }; 20 | "FloatingPointApproximation::FloatingPointApproximation::Product" = { 21 | isa = "PBXFileReference"; 22 | path = "FloatingPointApproximation.framework"; 23 | sourceTree = "BUILT_PRODUCTS_DIR"; 24 | }; 25 | "FloatingPointApproximation::FloatingPointApproximationPackageTests::ProductTarget" = { 26 | isa = "PBXAggregateTarget"; 27 | buildConfigurationList = "OBJ_32"; 28 | buildPhases = ( 29 | ); 30 | dependencies = ( 31 | "OBJ_35" 32 | ); 33 | name = "FloatingPointApproximationPackageTests"; 34 | productName = "FloatingPointApproximationPackageTests"; 35 | }; 36 | "FloatingPointApproximation::FloatingPointApproximationTests" = { 37 | isa = "PBXNativeTarget"; 38 | buildConfigurationList = "OBJ_37"; 39 | buildPhases = ( 40 | "OBJ_40", 41 | "OBJ_43" 42 | ); 43 | dependencies = ( 44 | "OBJ_45" 45 | ); 46 | name = "FloatingPointApproximationTests"; 47 | productName = "FloatingPointApproximationTests"; 48 | productReference = "FloatingPointApproximation::FloatingPointApproximationTests::Product"; 49 | productType = "com.apple.product-type.bundle.unit-test"; 50 | }; 51 | "FloatingPointApproximation::FloatingPointApproximationTests::Product" = { 52 | isa = "PBXFileReference"; 53 | path = "FloatingPointApproximationTests.xctest"; 54 | sourceTree = "BUILT_PRODUCTS_DIR"; 55 | }; 56 | "FloatingPointApproximation::SwiftPMPackageDescription" = { 57 | isa = "PBXNativeTarget"; 58 | buildConfigurationList = "OBJ_26"; 59 | buildPhases = ( 60 | "OBJ_29" 61 | ); 62 | dependencies = ( 63 | ); 64 | name = "FloatingPointApproximationPackageDescription"; 65 | productName = "FloatingPointApproximationPackageDescription"; 66 | productType = "com.apple.product-type.framework"; 67 | }; 68 | "OBJ_1" = { 69 | isa = "PBXProject"; 70 | attributes = { 71 | LastUpgradeCheck = "9999"; 72 | }; 73 | buildConfigurationList = "OBJ_2"; 74 | compatibilityVersion = "Xcode 3.2"; 75 | developmentRegion = "English"; 76 | hasScannedForEncodings = "0"; 77 | knownRegions = ( 78 | "en" 79 | ); 80 | mainGroup = "OBJ_5"; 81 | productRefGroup = "OBJ_15"; 82 | projectDirPath = "."; 83 | targets = ( 84 | "FloatingPointApproximation::FloatingPointApproximation", 85 | "FloatingPointApproximation::SwiftPMPackageDescription", 86 | "FloatingPointApproximation::FloatingPointApproximationPackageTests::ProductTarget", 87 | "FloatingPointApproximation::FloatingPointApproximationTests" 88 | ); 89 | }; 90 | "OBJ_10" = { 91 | isa = "PBXGroup"; 92 | children = ( 93 | "OBJ_11" 94 | ); 95 | name = "Tests"; 96 | path = ""; 97 | sourceTree = "SOURCE_ROOT"; 98 | }; 99 | "OBJ_11" = { 100 | isa = "PBXGroup"; 101 | children = ( 102 | "OBJ_12", 103 | "OBJ_13" 104 | ); 105 | name = "FloatingPointApproximationTests"; 106 | path = "Tests/FloatingPointApproximationTests"; 107 | sourceTree = "SOURCE_ROOT"; 108 | }; 109 | "OBJ_12" = { 110 | isa = "PBXFileReference"; 111 | path = "FloatingPointApproximationTests.swift"; 112 | sourceTree = ""; 113 | }; 114 | "OBJ_13" = { 115 | isa = "PBXFileReference"; 116 | path = "XCTestManifests.swift"; 117 | sourceTree = ""; 118 | }; 119 | "OBJ_14" = { 120 | isa = "PBXFileReference"; 121 | path = "FloatingPointApproximation.xcworkspace"; 122 | sourceTree = "SOURCE_ROOT"; 123 | }; 124 | "OBJ_15" = { 125 | isa = "PBXGroup"; 126 | children = ( 127 | "FloatingPointApproximation::FloatingPointApproximation::Product", 128 | "FloatingPointApproximation::FloatingPointApproximationTests::Product" 129 | ); 130 | name = "Products"; 131 | path = ""; 132 | sourceTree = "BUILT_PRODUCTS_DIR"; 133 | }; 134 | "OBJ_19" = { 135 | isa = "XCConfigurationList"; 136 | buildConfigurations = ( 137 | "OBJ_20", 138 | "OBJ_21" 139 | ); 140 | defaultConfigurationIsVisible = "0"; 141 | defaultConfigurationName = "Release"; 142 | }; 143 | "OBJ_2" = { 144 | isa = "XCConfigurationList"; 145 | buildConfigurations = ( 146 | "OBJ_3", 147 | "OBJ_4" 148 | ); 149 | defaultConfigurationIsVisible = "0"; 150 | defaultConfigurationName = "Release"; 151 | }; 152 | "OBJ_20" = { 153 | isa = "XCBuildConfiguration"; 154 | buildSettings = { 155 | ENABLE_TESTABILITY = "YES"; 156 | FRAMEWORK_SEARCH_PATHS = ( 157 | "$(inherited)", 158 | "$(PLATFORM_DIR)/Developer/Library/Frameworks" 159 | ); 160 | HEADER_SEARCH_PATHS = ( 161 | "$(inherited)" 162 | ); 163 | INFOPLIST_FILE = "FloatingPointApproximation.xcodeproj/FloatingPointApproximation_Info.plist"; 164 | LD_RUNPATH_SEARCH_PATHS = ( 165 | "$(inherited)", 166 | "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx" 167 | ); 168 | OTHER_CFLAGS = ( 169 | "$(inherited)" 170 | ); 171 | OTHER_LDFLAGS = ( 172 | "$(inherited)" 173 | ); 174 | OTHER_SWIFT_FLAGS = ( 175 | "$(inherited)" 176 | ); 177 | PRODUCT_BUNDLE_IDENTIFIER = "FloatingPointApproximation"; 178 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 179 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 180 | SKIP_INSTALL = "YES"; 181 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( 182 | "$(inherited)" 183 | ); 184 | SWIFT_VERSION = "4.2"; 185 | TARGET_NAME = "FloatingPointApproximation"; 186 | }; 187 | name = "Debug"; 188 | }; 189 | "OBJ_21" = { 190 | isa = "XCBuildConfiguration"; 191 | buildSettings = { 192 | ENABLE_TESTABILITY = "YES"; 193 | FRAMEWORK_SEARCH_PATHS = ( 194 | "$(inherited)", 195 | "$(PLATFORM_DIR)/Developer/Library/Frameworks" 196 | ); 197 | HEADER_SEARCH_PATHS = ( 198 | "$(inherited)" 199 | ); 200 | INFOPLIST_FILE = "FloatingPointApproximation.xcodeproj/FloatingPointApproximation_Info.plist"; 201 | LD_RUNPATH_SEARCH_PATHS = ( 202 | "$(inherited)", 203 | "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx" 204 | ); 205 | OTHER_CFLAGS = ( 206 | "$(inherited)" 207 | ); 208 | OTHER_LDFLAGS = ( 209 | "$(inherited)" 210 | ); 211 | OTHER_SWIFT_FLAGS = ( 212 | "$(inherited)" 213 | ); 214 | PRODUCT_BUNDLE_IDENTIFIER = "FloatingPointApproximation"; 215 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 216 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 217 | SKIP_INSTALL = "YES"; 218 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( 219 | "$(inherited)" 220 | ); 221 | SWIFT_VERSION = "4.2"; 222 | TARGET_NAME = "FloatingPointApproximation"; 223 | }; 224 | name = "Release"; 225 | }; 226 | "OBJ_22" = { 227 | isa = "PBXSourcesBuildPhase"; 228 | files = ( 229 | "OBJ_23" 230 | ); 231 | }; 232 | "OBJ_23" = { 233 | isa = "PBXBuildFile"; 234 | fileRef = "OBJ_9"; 235 | }; 236 | "OBJ_24" = { 237 | isa = "PBXFrameworksBuildPhase"; 238 | files = ( 239 | ); 240 | }; 241 | "OBJ_26" = { 242 | isa = "XCConfigurationList"; 243 | buildConfigurations = ( 244 | "OBJ_27", 245 | "OBJ_28" 246 | ); 247 | defaultConfigurationIsVisible = "0"; 248 | defaultConfigurationName = "Release"; 249 | }; 250 | "OBJ_27" = { 251 | isa = "XCBuildConfiguration"; 252 | buildSettings = { 253 | LD = "/usr/bin/true"; 254 | OTHER_SWIFT_FLAGS = ( 255 | "-swift-version", 256 | "4.2", 257 | "-I", 258 | "$(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2", 259 | "-target", 260 | "x86_64-apple-macosx10.10", 261 | "-sdk", 262 | "/Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk" 263 | ); 264 | SWIFT_VERSION = "4.2"; 265 | }; 266 | name = "Debug"; 267 | }; 268 | "OBJ_28" = { 269 | isa = "XCBuildConfiguration"; 270 | buildSettings = { 271 | LD = "/usr/bin/true"; 272 | OTHER_SWIFT_FLAGS = ( 273 | "-swift-version", 274 | "4.2", 275 | "-I", 276 | "$(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2", 277 | "-target", 278 | "x86_64-apple-macosx10.10", 279 | "-sdk", 280 | "/Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk" 281 | ); 282 | SWIFT_VERSION = "4.2"; 283 | }; 284 | name = "Release"; 285 | }; 286 | "OBJ_29" = { 287 | isa = "PBXSourcesBuildPhase"; 288 | files = ( 289 | "OBJ_30" 290 | ); 291 | }; 292 | "OBJ_3" = { 293 | isa = "XCBuildConfiguration"; 294 | buildSettings = { 295 | CLANG_ENABLE_OBJC_ARC = "YES"; 296 | COMBINE_HIDPI_IMAGES = "YES"; 297 | COPY_PHASE_STRIP = "NO"; 298 | DEBUG_INFORMATION_FORMAT = "dwarf"; 299 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 300 | ENABLE_NS_ASSERTIONS = "YES"; 301 | GCC_OPTIMIZATION_LEVEL = "0"; 302 | GCC_PREPROCESSOR_DEFINITIONS = ( 303 | "DEBUG=1", 304 | "$(inherited)" 305 | ); 306 | MACOSX_DEPLOYMENT_TARGET = "10.10"; 307 | ONLY_ACTIVE_ARCH = "YES"; 308 | OTHER_SWIFT_FLAGS = ( 309 | "-DXcode" 310 | ); 311 | PRODUCT_NAME = "$(TARGET_NAME)"; 312 | SDKROOT = "macosx"; 313 | SUPPORTED_PLATFORMS = ( 314 | "macosx", 315 | "iphoneos", 316 | "iphonesimulator", 317 | "appletvos", 318 | "appletvsimulator", 319 | "watchos", 320 | "watchsimulator" 321 | ); 322 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( 323 | "SWIFT_PACKAGE", 324 | "DEBUG" 325 | ); 326 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 327 | USE_HEADERMAP = "NO"; 328 | }; 329 | name = "Debug"; 330 | }; 331 | "OBJ_30" = { 332 | isa = "PBXBuildFile"; 333 | fileRef = "OBJ_6"; 334 | }; 335 | "OBJ_32" = { 336 | isa = "XCConfigurationList"; 337 | buildConfigurations = ( 338 | "OBJ_33", 339 | "OBJ_34" 340 | ); 341 | defaultConfigurationIsVisible = "0"; 342 | defaultConfigurationName = "Release"; 343 | }; 344 | "OBJ_33" = { 345 | isa = "XCBuildConfiguration"; 346 | buildSettings = { 347 | }; 348 | name = "Debug"; 349 | }; 350 | "OBJ_34" = { 351 | isa = "XCBuildConfiguration"; 352 | buildSettings = { 353 | }; 354 | name = "Release"; 355 | }; 356 | "OBJ_35" = { 357 | isa = "PBXTargetDependency"; 358 | target = "FloatingPointApproximation::FloatingPointApproximationTests"; 359 | }; 360 | "OBJ_37" = { 361 | isa = "XCConfigurationList"; 362 | buildConfigurations = ( 363 | "OBJ_38", 364 | "OBJ_39" 365 | ); 366 | defaultConfigurationIsVisible = "0"; 367 | defaultConfigurationName = "Release"; 368 | }; 369 | "OBJ_38" = { 370 | isa = "XCBuildConfiguration"; 371 | buildSettings = { 372 | CLANG_ENABLE_MODULES = "YES"; 373 | EMBEDDED_CONTENT_CONTAINS_SWIFT = "YES"; 374 | FRAMEWORK_SEARCH_PATHS = ( 375 | "$(inherited)", 376 | "$(PLATFORM_DIR)/Developer/Library/Frameworks" 377 | ); 378 | HEADER_SEARCH_PATHS = ( 379 | "$(inherited)" 380 | ); 381 | INFOPLIST_FILE = "FloatingPointApproximation.xcodeproj/FloatingPointApproximationTests_Info.plist"; 382 | LD_RUNPATH_SEARCH_PATHS = ( 383 | "$(inherited)", 384 | "@loader_path/../Frameworks", 385 | "@loader_path/Frameworks" 386 | ); 387 | OTHER_CFLAGS = ( 388 | "$(inherited)" 389 | ); 390 | OTHER_LDFLAGS = ( 391 | "$(inherited)" 392 | ); 393 | OTHER_SWIFT_FLAGS = ( 394 | "$(inherited)" 395 | ); 396 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( 397 | "$(inherited)" 398 | ); 399 | SWIFT_VERSION = "4.2"; 400 | TARGET_NAME = "FloatingPointApproximationTests"; 401 | }; 402 | name = "Debug"; 403 | }; 404 | "OBJ_39" = { 405 | isa = "XCBuildConfiguration"; 406 | buildSettings = { 407 | CLANG_ENABLE_MODULES = "YES"; 408 | EMBEDDED_CONTENT_CONTAINS_SWIFT = "YES"; 409 | FRAMEWORK_SEARCH_PATHS = ( 410 | "$(inherited)", 411 | "$(PLATFORM_DIR)/Developer/Library/Frameworks" 412 | ); 413 | HEADER_SEARCH_PATHS = ( 414 | "$(inherited)" 415 | ); 416 | INFOPLIST_FILE = "FloatingPointApproximation.xcodeproj/FloatingPointApproximationTests_Info.plist"; 417 | LD_RUNPATH_SEARCH_PATHS = ( 418 | "$(inherited)", 419 | "@loader_path/../Frameworks", 420 | "@loader_path/Frameworks" 421 | ); 422 | OTHER_CFLAGS = ( 423 | "$(inherited)" 424 | ); 425 | OTHER_LDFLAGS = ( 426 | "$(inherited)" 427 | ); 428 | OTHER_SWIFT_FLAGS = ( 429 | "$(inherited)" 430 | ); 431 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( 432 | "$(inherited)" 433 | ); 434 | SWIFT_VERSION = "4.2"; 435 | TARGET_NAME = "FloatingPointApproximationTests"; 436 | }; 437 | name = "Release"; 438 | }; 439 | "OBJ_4" = { 440 | isa = "XCBuildConfiguration"; 441 | buildSettings = { 442 | CLANG_ENABLE_OBJC_ARC = "YES"; 443 | COMBINE_HIDPI_IMAGES = "YES"; 444 | COPY_PHASE_STRIP = "YES"; 445 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 446 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 447 | GCC_OPTIMIZATION_LEVEL = "s"; 448 | MACOSX_DEPLOYMENT_TARGET = "10.10"; 449 | OTHER_SWIFT_FLAGS = ( 450 | "-DXcode" 451 | ); 452 | PRODUCT_NAME = "$(TARGET_NAME)"; 453 | SDKROOT = "macosx"; 454 | SUPPORTED_PLATFORMS = ( 455 | "macosx", 456 | "iphoneos", 457 | "iphonesimulator", 458 | "appletvos", 459 | "appletvsimulator", 460 | "watchos", 461 | "watchsimulator" 462 | ); 463 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( 464 | "SWIFT_PACKAGE" 465 | ); 466 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 467 | USE_HEADERMAP = "NO"; 468 | }; 469 | name = "Release"; 470 | }; 471 | "OBJ_40" = { 472 | isa = "PBXSourcesBuildPhase"; 473 | files = ( 474 | "OBJ_41", 475 | "OBJ_42" 476 | ); 477 | }; 478 | "OBJ_41" = { 479 | isa = "PBXBuildFile"; 480 | fileRef = "OBJ_12"; 481 | }; 482 | "OBJ_42" = { 483 | isa = "PBXBuildFile"; 484 | fileRef = "OBJ_13"; 485 | }; 486 | "OBJ_43" = { 487 | isa = "PBXFrameworksBuildPhase"; 488 | files = ( 489 | "OBJ_44" 490 | ); 491 | }; 492 | "OBJ_44" = { 493 | isa = "PBXBuildFile"; 494 | fileRef = "FloatingPointApproximation::FloatingPointApproximation::Product"; 495 | }; 496 | "OBJ_45" = { 497 | isa = "PBXTargetDependency"; 498 | target = "FloatingPointApproximation::FloatingPointApproximation"; 499 | }; 500 | "OBJ_5" = { 501 | isa = "PBXGroup"; 502 | children = ( 503 | "OBJ_6", 504 | "OBJ_7", 505 | "OBJ_10", 506 | "OBJ_14", 507 | "OBJ_15" 508 | ); 509 | path = ""; 510 | sourceTree = ""; 511 | }; 512 | "OBJ_6" = { 513 | isa = "PBXFileReference"; 514 | explicitFileType = "sourcecode.swift"; 515 | path = "Package.swift"; 516 | sourceTree = ""; 517 | }; 518 | "OBJ_7" = { 519 | isa = "PBXGroup"; 520 | children = ( 521 | "OBJ_8" 522 | ); 523 | name = "Sources"; 524 | path = ""; 525 | sourceTree = "SOURCE_ROOT"; 526 | }; 527 | "OBJ_8" = { 528 | isa = "PBXGroup"; 529 | children = ( 530 | "OBJ_9" 531 | ); 532 | name = "FloatingPointApproximation"; 533 | path = "Sources/FloatingPointApproximation"; 534 | sourceTree = "SOURCE_ROOT"; 535 | }; 536 | "OBJ_9" = { 537 | isa = "PBXFileReference"; 538 | path = "FloatingPoint+Approximation.swift"; 539 | sourceTree = ""; 540 | }; 541 | }; 542 | rootObject = "OBJ_1"; 543 | } 544 | -------------------------------------------------------------------------------- /FloatingPointApproximation.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /FloatingPointApproximation.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /FloatingPointApproximation.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | -------------------------------------------------------------------------------- /FloatingPointApproximation.xcodeproj/xcshareddata/xcschemes/FloatingPointApproximation.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /FloatingPointApproximation.xcodeproj/xcshareddata/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SchemeUserState 5 | 6 | FloatingPointApproximation-Package.xcscheme 7 | 8 | 9 | SuppressBuildableAutocreation 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /FloatingPointApproximation.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /FloatingPointApproximation.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 Read Evaluate Press, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "FloatingPointApproximation", 8 | products: [ 9 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 10 | .library( 11 | name: "FloatingPointApproximation", 12 | targets: ["FloatingPointApproximation"]), 13 | ], 14 | dependencies: [ 15 | // Dependencies declare other packages that this package depends on. 16 | // .package(url: /* package url */, from: "1.0.0"), 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 21 | .target( 22 | name: "FloatingPointApproximation", 23 | dependencies: []), 24 | .testTarget( 25 | name: "FloatingPointApproximationTests", 26 | dependencies: ["FloatingPointApproximation"]), 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FloatingPointApproximation 2 | 3 | [![Build Status][build status badge]][build status] 4 | 5 | A correct way to determine if two floating-point numbers 6 | are approximately equal to one another. 7 | 8 | This functionality is discussed in Chapter 3 of 9 | [Flight School Guide to Swift Numbers](https://flight.school/books/numbers). 10 | 11 | ## Requirements 12 | 13 | - Swift 4.0+ 14 | 15 | ## Installation 16 | 17 | ### Swift Package Manager 18 | 19 | Add the FloatingPointApproximation package to your target dependencies in `Package.swift`: 20 | 21 | ```swift 22 | import PackageDescription 23 | 24 | let package = Package( 25 | name: "YourProject", 26 | dependencies: [ 27 | .package( 28 | url: "https://github.com/Flight-School/FloatingPointApproximation", 29 | from: "1.0.0" 30 | ), 31 | ] 32 | ) 33 | ``` 34 | 35 | Then run the `swift build` command to build your project. 36 | 37 | ### Carthage 38 | 39 | To use `FloatingPointApproximation` in your Xcode project using Carthage, 40 | specify it in `Cartfile`: 41 | 42 | ``` 43 | github "Flight-School/FloatingPointApproximation" ~> 1.0.0 44 | ``` 45 | 46 | Then run the `carthage update` command to build the framework, 47 | and drag the built FloatingPointApproximation.framework into your Xcode project. 48 | 49 | ## Usage 50 | 51 | Floating-point arithmetic can produce unexpected results, 52 | such as `0.1 + 0.2 != 0.3`. 53 | The reason for this is that many fractional numbers, 54 | including `0.1`, `0.2`, and `0.3`, 55 | cannot be precisely expressed in a binary number representation. 56 | 57 | A common mistake is to use an arbitrarily small constant 58 | (such as `.ulpOfOne`) 59 | to determine whether two floating-point numbers are approximately equal. 60 | For example: 61 | 62 | ```swift 63 | let actual = 0.1 + 0.2 64 | let expected = 0.3 65 | abs(expected - actual) < .ulpOfOne // true 66 | ``` 67 | 68 | However, this doesn't work for large scale numbers: 69 | 70 | ```swift 71 | let actual = 1e25 + 2e25 72 | let expected = 3e25 73 | abs(expected - actual) < .ulpOfOne // false 74 | ``` 75 | 76 | A better approach for determining approximate equality 77 | would be to count how many representable values, or ULPs, 78 | exist between two floating-point numbers. 79 | 80 | The `==~` operator (and its complement, `!=~`) 81 | defined by this package 82 | returns a Boolean value indicating whether 83 | two floating-point numbers are approximately equal. 84 | 85 | ```swift 86 | import FloatingPointApproximation 87 | 88 | 0.1 + 0.2 == 0.3 // false 89 | 0.1 + 0.2 ==~ 0.3 // true 90 | ``` 91 | 92 | Floating-point numbers are defined to be approximately equal 93 | if they are within one _unit of least precision_, or 94 | [ULP](https://en.wikipedia.org/wiki/Unit_in_the_last_place), 95 | of one another. 96 | 97 | Because of how Swift implements floating-point numbers, 98 | the implementation of the `==~` operator is quite simple: 99 | 100 | ```swift 101 | func ==~ (lhs: T, rhs: T) -> Bool where T: FloatingPoint { 102 | return lhs == rhs || lhs.nextDown == rhs || lhs.nextUp == rhs 103 | } 104 | ``` 105 | 106 | A more complete approach combines both absolute and relative comparisons. 107 | The `isApproximatelyEqual(to:within:maximumULPs:)` method 108 | determines whether a floating-point number 109 | is approximately equal to another value 110 | by first checking to see if it is within a given absolute margin, if provided, 111 | and then checking to see if it falls within a given number of ULPs: 112 | 113 | ```swift 114 | import FloatingPointApproximation 115 | 116 | (0.1 + 0.2).isApproximatelyEqual(to: 0.3, within: 1e-12, maximumULPs: 2) 117 | ``` 118 | 119 | Ultimately, it's your responsibility to determine how to compare 120 | two floating-point numbers for approximate equality 121 | based on the requirements of your domain. 122 | 123 | ## License 124 | 125 | MIT 126 | 127 | ## Contact 128 | 129 | Mattt ([@mattt](https://twitter.com/mattt)) 130 | 131 | [build status]: https://travis-ci.org/Flight-School/FloatingPointApproximation 132 | [build status badge]: https://api.travis-ci.com/Flight-School/FloatingPointApproximation.svg?branch=master 133 | -------------------------------------------------------------------------------- /Sources/FloatingPointApproximation/FloatingPoint+Approximation.swift: -------------------------------------------------------------------------------- 1 | infix operator ==~ : ComparisonPrecedence 2 | /** 3 | Returns a Boolean value indicating whether 4 | two floating-point numbers are approximately equal. 5 | 6 | Floating-point numbers are defined to be approximately equal 7 | if they are within one _unit of least precision_, or ULP, 8 | of one another. 9 | */ 10 | public func ==~ (lhs: T, rhs: T) -> Bool where T: FloatingPoint { 11 | return lhs == rhs || lhs.nextDown == rhs || lhs.nextUp == rhs 12 | } 13 | 14 | infix operator !=~ : ComparisonPrecedence 15 | /** 16 | Returns a Boolean value indicating whether 17 | two floating-point numbers are not approximately equal. 18 | 19 | - SeeAlso: ==~ 20 | */ 21 | public func !=~ (lhs: T, rhs: T) -> Bool where T: FloatingPoint { 22 | return !(lhs ==~ rhs) 23 | } 24 | 25 | extension FloatingPoint { 26 | /** 27 | Returns a Boolean value indicating whether 28 | the floating-point number is approximately equal to another value 29 | within a given absolute margin and/or 30 | maximum number of _units of least precision_, or ULPs. 31 | 32 | - Parameter other: The value to compare to this number. 33 | - Parameter margin: The maximum difference for approximate equality. 34 | Must not be negative. 35 | - Parameter ulps: The maximum number of units of least precision 36 | for approximate equality. 37 | Must be greater than zero. 38 | */ 39 | public func isApproximatelyEqual(to other: Self, 40 | within margin: Self?, 41 | maximumULPs ulps: Int = 1) -> Bool 42 | { 43 | precondition(margin?.sign != .minus && ulps > 0) 44 | 45 | guard self != other else { 46 | return true 47 | } 48 | 49 | let distance = abs(self - other) 50 | 51 | if let margin = margin, distance > margin { 52 | return false 53 | } else { 54 | return distance <= (self.ulp * Self(ulps)) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/FloatingPointApproximationTests/FloatingPointApproximationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import FloatingPointApproximation 3 | 4 | final class FloatingPointApproximationTests: XCTestCase { 5 | func testEqualToOperator() { 6 | XCTAssertFalse(0.1 + 0.2 == 0.3) 7 | XCTAssertTrue(0.1 == 0.1) 8 | } 9 | 10 | func testApproximatelyEqualToOperator() { 11 | XCTAssertTrue(0.1 + 0.2 ==~ 0.3) 12 | XCTAssertTrue(0.1 ==~ 0.1) 13 | XCTAssertFalse(0.1 ==~ 0.2) 14 | XCTAssertTrue(0.1 ==~ 0.1.nextDown) 15 | XCTAssertTrue(0.1 ==~ 0.1.nextUp) 16 | XCTAssertFalse(0.1.nextDown ==~ 0.1.nextUp) 17 | 18 | XCTAssertTrue(-0.0 ==~ 0.0) 19 | XCTAssertTrue(Double.infinity ==~ Double.infinity) 20 | XCTAssertFalse(Double.infinity ==~ -Double.infinity) 21 | XCTAssertFalse(Double.nan ==~ Double.nan) 22 | } 23 | 24 | func testIsApproximatelyEqualToWithin() { 25 | XCTAssertTrue((0.1 + 0.2).isApproximatelyEqual(to: 0.3, 26 | within: 1e-12, 27 | maximumULPs: 2)) 28 | } 29 | 30 | static var allTests = [ 31 | ("testEqualToOperator", testEqualToOperator), 32 | ("testApproximatelyEqualToOperator", testApproximatelyEqualToOperator), 33 | ("testIsApproximatelyEqualToWithin", testIsApproximatelyEqualToWithin) 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /Tests/FloatingPointApproximationTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !os(macOS) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(FloatingPointApproximationTests.allTests), 7 | ] 8 | } 9 | #endif -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import FloatingPointApproximationTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += FloatingPointApproximationTests.allTests() 7 | XCTMain(tests) --------------------------------------------------------------------------------