Literal Numbers in Floatingpoint Protocol

Literal numbers in FloatingPoint protocol

The FloatingPoint protocol inherits from ExpressibleByIntegerLiteral
via the inheritance chain

FloatingPoint - SignedNumeric - Numeric - ExpressibleByIntegerLiteral

and that is why the second function uprightAngle2 compiles: Values
of the type T are created from the integer literals 2 and 3.

The first function uprightAngle1 does not compile because
the FloatingPoint protocol does not inherit from ExpressibleByFloatLiteral, i.e. values of type T can not
be created from a floating point literal like 1.5.

Possible solutions:

  • Create rational values as let half: T = 1/2. (Not let half = T(1/2),
    that would truncate the division result before creating the T
    value.)

  • Replace FloatingPoint by BinaryFloatingPoint (which inherits
    from ExpressibleByFloatLiteral).

For more information about the design of the floating point
protocols see SE-0067 Enhanced Floating Point Protocols.

The floating point types in the Swift Standard Library (Double, Float, CGFloat, Float80) as well as
CGFloat from the Core Graphics framework all conform to the
BinaryFloatingPoint protocol, so this protocol is "sufficiently
generic" for many applications.

How to use FloatingPoint generic type for Float/Double

One problem is that FloatingPoint is not a subprotocol of ExpressibleByFloatLiteral, so your floating-point literals cannot necessarily be converted to T. You can solve this either by changing FloatingPoint to BinaryFloatingPoint (which is a subprotocol of ExpressibleByFloatLiteral) or by adding ExpressibleByFloatLiteral as a separate requirement.

Then you will run into the problem that there is no pow function that is generic over FloatingPoint, and no member of FloatingPoint or BinaryFloatingPoint that performs exponentiation. You can solve this by creating a new protocol and conforming the existing floating-point types to it:

protocol Exponentiatable {
func toPower(_ power: Self) -> Self
}

extension Float: Exponentiatable {
func toPower(_ power: Float) -> Float { return pow(self, power) }
}

extension Double: Exponentiatable {
func toPower(_ power: Double) -> Double { return pow(self, power) }
}

extension CGFloat: Exponentiatable {
func toPower(_ power: CGFloat) -> CGFloat { return pow(self, power) }
}

Note that there is also a Float80 type, but the standard library doesn't provide a pow function for it.

Now we can write a working generic function:

func srgb2linear<T: FloatingPoint>(_ S: T) -> T
where T: ExpressibleByFloatLiteral, T: Exponentiatable
{
if S <= 0.04045 {
return S / 12.92
} else {
return ((S + 0.055) / 1.055).toPower(2.4)
}
}

Coding conventions for number literal suffix?

From the Java Tutorials:

Floating-Point Literals

A floating-point literal is of type float if it ends with the letter F or f; otherwise its type is double and it can optionally end with the letter D or d.

The floating point types (float and double) can also be expressed using E or e (for scientific notation), F or f (32-bit float literal) and D or d (64-bit double literal; this is the default and by convention is omitted).

double d1 = 123.4;
// same value as d1, but in scientific notation
double d2 = 1.234e2;
float f1 = 123.4f;

This tells you that:

  • Both D and d have the same meaning - just as you observed
  • "By convention [D or d are] omitted" because they are the default

Questions of the type "what is better" are offtopic on StackOverflow since they can't have a correct or incorrect answer, only opinions.

But if you ask for a convention I would tend to follow what I've found above - omit the suffix.

Literals in Swift generics

You can create a type constraint (MyFloats below) to which your floating point types conform. You let this type constraint itself conform to Comparable, so that you may make use of the less than binary infix operator < when comparing the values of your generics. Also, for your example given above, the MyFloats type constraint need contain only a single blueprint; an initializer for a Double argument. This initalizer already exists for Double, Float and CGFloat types, but since a protocol cannot know which types that conforms to it, you need to include this blueprint.

protocol MyFloats : Comparable {
init(_ value: Double)
}

extension Double : MyFloats { }
extension Float : MyFloats { }
extension CGFloat : MyFloats { }

func sign<T: MyFloats> (value:T) -> T {
if value < T(0.0) {
return T(-1.0)
}
if value > T(0.0) {
return T(1.0)
}
return T(0.0)
}

Is there any C++ style guide that talks about numeric literal suffixes?

There is no general style guide that I've found. I use capital letters and I'm picky about using F for float literals and L for long double. I also use the appropriate suffixes for integral literals.

I assume you know what these suffixes mean: 3.14F is a float literal, 12.345 is a double literal, 6.6666L is a long double literal.

For integers: U is unsigned, L is long, LL is long long. Order between U and the Ls doesn't matter but I always put UL because I declare such variables unsigned long for example.

If you assign a variable of one type a literal of another type, or supply a numeric literal of one type for function argument of another type a cast must happen. Using the proper suffix avoids this and is useful along the same lines as static_cast is useful for calling out casts. Consistent usage of numeric literal suffixes is good style and avoids numeric surprises.

People differ on whether lower or upper case is best. Pick a style that looks good to you and be consistent.

Why can a numeric literal be interpreted as an NSNumber in Swift?

NSNumber
conforms to the ExpressibleByBooleanLiteral, ExpressibleByFloatLiteral
and ExpressibleByIntegerLiteral protocols, which means that it can be
initialized from a boolean, floating point, or integer literal:

let n1: NSNumber = false
let n2: NSNumber = 12.34
let n3: NSNumber = 123


Related Topics



Leave a reply



Submit