├── .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)
--------------------------------------------------------------------------------