<div dir="ltr">This would be an excellent solution to the issue.<div>Do you know if there are any existing plans for something like the DecimalLiteralConvertible?</div><div><br></div><div>Another thought:</div><div>Would it make sense to have the compiler warn about float literal precision issues?</div><div>Initialization of two different variables with the exact same literal value could yield different precision results if one had a FloatLiteralType aliased to Float80 and the other aliased to Float.</div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr">On Fri, May 6, 2016 at 6:46 PM Joe Groff &lt;<a href="mailto:jgroff@apple.com">jgroff@apple.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
&gt; On May 6, 2016, at 9:42 AM, Stephen Canon &lt;<a href="mailto:scanon@apple.com" target="_blank">scanon@apple.com</a>&gt; wrote:<br>
&gt;<br>
&gt;<br>
&gt;&gt; On May 6, 2016, at 12:41 PM, Joe Groff via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:<br>
&gt;&gt;<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; On May 6, 2016, at 2:24 AM, Morten Bek Ditlevsen via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; Currently, in order to conform to FloatLiteralConvertible you need to implement<br>
&gt;&gt;&gt; an initializer accepting a floatLiteral of the typealias: FloatLiteralType.<br>
&gt;&gt;&gt; However, this typealias can only be Double, Float, Float80 and other built-in<br>
&gt;&gt;&gt; floating point types (to be honest, I do not know the exact limitation since I have<br>
&gt;&gt;&gt; not been able to read find this in the documentation).<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; These floating point types have precision limitations that are not necessarily<br>
&gt;&gt;&gt; present in the type that you are making FloatLiteralConvertible.<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; Let’s imagine a CurrencyAmount type that uses an NSDecimalNumber as the<br>
&gt;&gt;&gt; representation of the value:<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; public struct CurrencyAmount {<br>
&gt;&gt;&gt; public let value: NSDecimalNumber<br>
&gt;&gt;&gt; // .. other important currency-related stuff ..<br>
&gt;&gt;&gt; }<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; extension CurrencyAmount: FloatLiteralConvertible {<br>
&gt;&gt;&gt; public typealias FloatLiteralType = Double<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; public init(floatLiteral amount: FloatLiteralType) {<br>
&gt;&gt;&gt;   print(amount.debugDescription)<br>
&gt;&gt;&gt;   value = NSDecimalNumber(double: amount)<br>
&gt;&gt;&gt; }<br>
&gt;&gt;&gt; }<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; let a: CurrencyAmount = 99.99<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; The printed value inside the initializer is 99.989999999999995 - so the value<br>
&gt;&gt;&gt; has lost precision already in the intermediary Double representation.<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; I know that there is also an issue with the NSDecimalNumber double initializer,<br>
&gt;&gt;&gt; but this is not the issue that we are seeing here.<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; One suggestion for a solution to this issue would be to allow the<br>
&gt;&gt;&gt; FloatLiteralType to be aliased to a String.  In this case the compiler should<br>
&gt;&gt;&gt; parse the float literal token: 99.99 to a String and use that as input for the<br>
&gt;&gt;&gt; FloatLiteralConvertible initializer.<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; This would mean that arbitrary literal precisions are allowed for<br>
&gt;&gt;&gt; FloatLiteralConvertibles that implement their own parsing of a String value.<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; For instance, if the CurrencyAmount used a FloatLiteralType aliased to String we<br>
&gt;&gt;&gt; would have:<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; extension CurrencyAmount: FloatLiteralConvertible {<br>
&gt;&gt;&gt; public typealias FloatLiteralType = String<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; public init(floatLiteral amount: FloatLiteralType) {<br>
&gt;&gt;&gt;   value = NSDecimalNumber(string: amount)<br>
&gt;&gt;&gt; }<br>
&gt;&gt;&gt; }<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; and the precision would be the same as creating an NSDecimalNumber from a<br>
&gt;&gt;&gt; String:<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; let a: CurrencyAmount = 1.00000000000000000000000000000000001<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; print(a.value.debugDescription)<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; Would give: 1.00000000000000000000000000000000001<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; How does that sound? Is it completely irrational to allow the use of Strings as<br>
&gt;&gt;&gt; the intermediary representation of float literals?<br>
&gt;&gt;&gt; I think that it makes good sense, since it allows for arbitrary precision.<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; Please let me know what you think.<br>
&gt;&gt;<br>
&gt;&gt; Like Dmitri said, a String is not a particularly efficient intermediate representation. For common machine numeric types, we want it to be straightforward for the compiler to constant-fold literals down to constants in the resulting binary. For floating-point literals, I think we could achieve this by changing the protocol to &quot;deconstruct&quot; the literal value into integer significand and exponent, something like this:<br>
&gt;&gt;<br>
&gt;&gt; // A type that can be initialized from a decimal literal such as<br>
&gt;&gt; // `1.1` or `2.3e5`.<br>
&gt;&gt; protocol DecimalLiteralConvertible {<br>
&gt;&gt;  // The integer type used to represent the significand and exponent of the value.<br>
&gt;&gt;  typealias Component: IntegerLiteralConvertible<br>
&gt;&gt;<br>
&gt;&gt;  // Construct a value equal to `decimalSignificand * 10**decimalExponent`.<br>
&gt;&gt;  init(decimalSignificand: Component, decimalExponent: Component)<br>
&gt;&gt; }<br>
&gt;&gt;<br>
&gt;&gt; // A type that can be initialized from a hexadecimal floating point<br>
&gt;&gt; // literal, such as `0x1.8p-2`.<br>
&gt;&gt; protocol HexFloatLiteralConvertible {<br>
&gt;&gt;  // The integer type used to represent the significand and exponent of the value.<br>
&gt;&gt;  typealias Component: IntegerLiteralConvertible<br>
&gt;&gt;<br>
&gt;&gt;  // Construct a value equal to `hexadecimalSignificand * 2**binaryExponent`.<br>
&gt;&gt;  init(hexadecimalSignificand: Component, binaryExponent: Component)<br>
&gt;&gt; }<br>
&gt;&gt;<br>
&gt;&gt; Literals would desugar to constructor calls as follows:<br>
&gt;&gt;<br>
&gt;&gt; 1.0 // T(decimalSignificand: 1, decimalExponent: 0)<br>
&gt;&gt; 0.123 // T(decimalSignificand: 123, decimalExponent: -3)<br>
&gt;&gt; 1.23e-2 // same<br>
&gt;&gt;<br>
&gt;&gt; 0x1.8p-2 // T(hexadecimalSignificand: 0x18, binaryExponent: -6)<br>
&gt;<br>
&gt; This seems like a very good approach to me.<br>
<br>
It occurs to me that &quot;sign&quot; probably needs to be an independent parameter, to be able to accurately capture literal -0 and 0:<br>
<br>
// A type that can be initialized from a decimal literal such as<br>
// `1.1` or `-2.3e5`.<br>
protocol DecimalLiteralConvertible {<br>
 // The integer type used to represent the significand and exponent of the value.<br>
 typealias Component: IntegerLiteralConvertible<br>
<br>
 // Construct a value equal to `decimalSignificand * 10**decimalExponent * (isNegative ? -1 : 1)`.<br>
 init(decimalSignificand: Component, decimalExponent: Component, isNegative: Bool)<br>
}<br>
<br>
// A type that can be initialized from a hexadecimal floating point<br>
// literal, such as `0x1.8p-2`.<br>
protocol HexFloatLiteralConvertible {<br>
 // The integer type used to represent the significand and exponent of the value.<br>
 typealias Component: IntegerLiteralConvertible<br>
<br>
 // Construct a value equal to `hexadecimalSignificand * 2**binaryExponent * (isNegative ? -1 : 1)`.<br>
 init(hexadecimalSignificand: Component, binaryExponent: Component, isNegative: Bool)<br>
}<br>
<br>
-Joe</blockquote></div>