<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">I agree with all of this; I don’t really know enough to comment on the specific implementation of a decimal type, but we definitely need something other than NSDecimal.</div><div class=""><br class=""></div><div class="">I don’t know if it’s possible, but I think that tolerances should be able to take a percentage. For example, I could write 0.0000001±1%, which would be <b class="">much</b> clearer (and less error prone) than 0.0000001± 0.000000001</div><div class=""><br class=""></div><div class="">This is also useful because I think we could also benefit from the addition of tolerances to types, allowing us to declare something like: var foo:Float±0.01 = 0, which specifies a floating point value that the Swift compiler will not allow values to be added/substracted etc. to/from if they have a tolerance higher than my requirement. While cumulative error could still result in issues, if my tolerance is set reasonably low for my use-case then it would allow me to limit how much error I can accumulate in the lifetime of my data. In this being able to specify a percentage is useful when the variable has a clear right hand side such as var foo:Float±5% = 0.1 (effectively var foo:Float±0.005 = 0.1).</div><br class=""><div><blockquote type="cite" class=""><div class="">On 18 Mar 2016, at 22:42, Rainer Brockerhoff via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class="">First draft towards a tentative pre-proposal:<br class=""><a href="https://gist.github.com/rbrockerhoff/6874a5698bb479886e83" class="">https://gist.github.com/rbrockerhoff/6874a5698bb479886e83</a><br class="">------<br class=""><br class="">Pre-proposal: Safer Decimal Calculations<br class="">Proposal: TBD<br class="">Author(s): Rainer Brockerhoff<br class="">Status: TBD<br class="">Review manager: TBD<br class=""><br class="">Quoting the “The Swift Programming Language” book: “Swift adopts safe<br class="">programming patterns…”; “Swift is friendly to new programmers”. The<br class="">words “safe” and “safety” are found many times in the book and in online<br class="">documentation. The usual rationale for safe features is, to quote a<br class="">typical sentence, “…enables you to catch and fix errors as early as<br class="">possible in the development process”.<br class=""><br class="">One frequent stumbling point for both new and experienced programmers<br class="">stems from the vagaries of binary floating-point arithmetic. This<br class="">tentative pre-proposal suggests one possible way to make the dangers<br class="">somewhat more clear.<br class=""><br class="">My intention here is to start a discussion on this to inform the ongoing<br class="">(and future) reasoning on extending and regularising arithmetic in Swift.<br class=""><br class="">Motivation<br class=""><br class="">Floating-point hardware on most platforms that run Swift — that is,<br class="">Intel and ARM CPUs — uses the binary representation forms of the IEEE<br class="">754-2008 standard. Although some few mainframes and software libraries<br class="">implement the decimal representations this is not currently leveraged by<br class="">Swift. Apple's NSDecimal and NSDecimalNumber implementation is awkward<br class="">to use in Swift, especially as standard arithmetic operators cannot be<br class="">used directly.<br class=""><br class="">Although it is possible to express floating-point constants in<br class="">hexadecimal (0x123.AB) with an optional binary exponent (0x123A.Bp-4),<br class="">decimal-form floating-point constants (123.45 or 1.2345e2) are extremely<br class="">common in practice.<br class=""><br class="">Unfortunately it is tempting to use floating-point arithmetic for<br class="">financial calculations or other purposes such as labelling graphical or<br class="">statistical data. Constants such as 0.1, 0.01, 0.001 and variations or<br class="">multiples thereof will certainly be used in such applications — and<br class="">almost none of these constant can be precisely represented in binary<br class="">floating-point format.<br class=""><br class="">Rounding errors will therefore be introduced at the outset, causing<br class="">unexpected or outright buggy behaviour down the line which will be<br class="">surprising to the user and/or the programmer. This will often happen at<br class="">some point when the results of a calculation are compared to a constant<br class="">or to another result.<br class=""><br class="">Current Solution<br class=""><br class="">As things stand, Swift's default print() function, Xcode playgrounds<br class="">etc. do some discreet rounding or truncation to make the problem less<br class="">apparent - a Double initialized with the literal 0.1 prints out as 0.1<br class="">instead of the exact value of the internal representation, something<br class="">like 0.100000000000000005551115123125782702118158340454101562.<br class=""><br class="">This, unfortunately, masks this underlying problem in settings such as<br class="">“toy” programs or educational playgrounds, leading programmers to be<br class="">surprised later when things won't work. A cursory search on<br class="">StackOverflow reveals tens of thousands of questions with headings like<br class="">“Is floating point math broken?".<br class=""><br class="">Warning on imprecise literals<br class=""><br class="">To make decimal-format floating-point literals safe, I suggest that the<br class="">compiler should emit a warning whenever a literal is used that cannot be<br class="">safely represented as an exact value of the type expected. (Note that<br class="">0.1 cannot be represented exactly as any binary floating-point type.)<br class=""><br class="">The experienced programmer will, however, be willing to accept some<br class="">imprecision under circumstances that cannot be reliably determined by<br class="">the compiler. I suggest, therefore, that this acceptance be indicated by<br class="">an annotation to the literal; a form such as ~0.1 might be easiest to<br class="">read and implement, as the prefix ~ operator currently has no meaning<br class="">for a floating-point value. A “fixit” would be easily implemented to<br class="">insert the missing notation.<br class=""><br class="">Conversely, to avoid inexperienced or hurried programmers to strew ~s<br class="">everywhere, it would be useful to warn, and offer to fix, if the ~ is<br class="">present but the literal does have an exact representation.<br class=""><br class="">Tolerances<br class=""><br class="">A parallel idea is that of tolerances, introducing an ‘epsilon’ value to<br class="">be used in comparisons. Unfortunately an effective value of the epsilon<br class="">depends on the magnitude of the operands and there are many edge cases.<br class=""><br class="">Introducing a special type along the lines of “floating point with<br class="">tolerances” — using some accepted engineering notation for literals like<br class="">100.5±0.1 — might be useful for specialised applications but will not<br class="">solve this specific problem. Expanding existing constructs to accept an<br class="">optional tolerance value, as has been proposed elsewhere, may be useful<br class="">in those specific instances but not contribute to raise programmer<br class="">awareness of unsafe literals.<br class=""><br class="">Full Decimal type proposal<br class=""><br class="">There are cogent arguments that prior art/habits and the already complex<br class="">interactions between Double, Float, Float80 and CGFloat are best left alone.<br class=""><br class="">However, there remains a need for a precise implementation of a workable<br class="">Decimal value type for financial calculations. IMHO repurposing the<br class="">existing NSDecimalNumber from Objective-C is not the best solution.<br class=""><br class="">As most experienced developers know, the standard solution for financial<br class="">calculations is to internally store fixed-point values — usually but not<br class="">always in cents — and then print the “virtual” point (or decimal comma,<br class="">for the rest of us) on output.<br class=""><br class="">I propose, therefore, an internal data layout like this:<br class=""><br class="">UInt16 - position of the “virtual” point, starting at 0<br class="">UInt16 - data array size - 1<br class="">[Int32] - contiguous data array, little-endian order, grown as needed.<br class="">Note that both UInt16 fields being zero implies that the number is<br class="">reduced to a 32-bit Integer. Number literals in Swift can be up to 2048<br class="">bits in size, so the maximum data array size would be 64, although it<br class="">could conceivably grow beyond that. The usual cases of the virtual point<br class="">position being 0 or 2 could be aggressively optimized for normal<br class="">arithmetic operators.<br class=""><br class="">Needless to say such a Decimal number would accept and represent<br class="">literals such as 0.01 with no problems. It would also serve as a BigNum<br class="">implementation for most purposes.<br class=""><br class="">No doubt implementing this type in the standard library would allow for<br class="">highly optimized implementations for all major CPU platforms. In<br class="">particular, the data array should probably be [Int64] for 64-bit platforms.<br class=""><br class="">Acknowledgement<br class=""><br class="">Thanks to Erica Sadun for their help with an early version of this<br class="">pre-proposal.<br class=""><br class="">Some references<br class=""><br class="">http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html<br class="">https://docs.python.org/2/tutorial/floatingpoint.html<br class="">https://en.wikipedia.org/wiki/IEEE_floating_point<br class="">https://randomascii.wordpress.com/category/floating-point/<br class="">http://code.jsoftware.com/wiki/Essays/Tolerant_Comparison<br class=""><br class="">-- <br class="">Rainer Brockerhoff <rainer@brockerhoff.net><br class="">Belo Horizonte, Brazil<br class="">"In the affairs of others even fools are wise<br class="">In their own business even sages err."<br class="">http://brockerhoff.net/blog/<br class="">_______________________________________________<br class="">swift-evolution mailing list<br class="">swift-evolution@swift.org<br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></body></html>