Make Swift Assume Degrees for Trigonometry Calculations

旧城冷巷雨未停 提交于 2019-11-26 21:41:00

问题


Is it possible to change a setting, property, etc in Swift for iOS so that it assumes degrees for trigonometry calculations rather than radians?

For example sin(90) would be evaluated to 1.

I have:

let pi = 3.14 
var r2d = 180.0/pi
var d2r = pi/180

... but the conversions are getting really involved for some of the long trig equations.


回答1:


As already said in the other answers, there are no trigonometric functions in the standard library that take the arguments in degrees.

If you define your own function then you can use __sinpi(), __cospi(), etc ... instead of multiplying by π:

// Swift 2:
func sin(degrees degrees: Double) -> Double {
    return __sinpi(degrees/180.0)
}

// Swift 3:
func sin(degrees: Double) -> Double {
    return __sinpi(degrees/180.0)
}

From the __sinpi manual page (emphasis added):

The __sinpi() function returns the sine of pi times x (measured in radians). This can be computed more accurately than sin(M_PI * x), because it can implicitly use as many bits of pi as are necessary to deliver a well-rounded result, instead of the 53-bits to which M_PI is limited. For large x it may also be more efficient, as the argument reduction involved is significantly simpler.

__sinpi() and the related functions are non-standard, but available on iOS 7/OS X 10.9 and later.

Example:

sin(degrees: 180.0)       // 0

gives an exact result, in contrast to:

sin(180.0 * M_PI/180.0) // 1.224646799147353e-16

And just for fun: This is how you can define the degree-based sine function for all floating point types, including CGFloat with function overloading (now updated for Swift 3):

func sin(degrees: Double) -> Double {
    return __sinpi(degrees/180.0)
}

func sin(degrees: Float) -> Float {
    return __sinpif(degrees/180.0)
}

func sin(degrees: CGFloat) -> CGFloat {
    return CGFloat(sin(degrees: degrees.native))
}

In the last variant, the compiler automatically infers from the actual type of degrees.native which function to call, so that this works correctly on both 32-bit and 64-bit platforms.




回答2:


Adding an extension to identify the kind of value clearly would be a appropriate way to handle such a thing:

import Darwin // needed to get M_PI
extension Double {
  public var degrees: Double { return self * M_PI / 180 }
  public var ㎭: Double { return self * 180 / M_PI }
}

Pop that into a playground and see how you get the results you expect:

sin(90.degrees)  --> 1.0
1.㎭  -->  57.2957795130823
1.㎭.degrees --> 1.0
(M_PI / 3).㎭  -->  60.0



回答3:


You can define global functions that return the sin of a degree value. Just place the function in a swift file outside of any class.

func sind(degrees: Double) -> Double {
    return sin(degrees * M_PI / 180.0)
}

So anywhere in your project you can just use:

sind(90) // Returns 1



回答4:


This runs in a playground, and provides a type-safe implementation of degree/radian units. The type definitions are freely taken from here on the Swift evolution mailing list with a couple of minor syntax fixes. I wrote in some of the trig functions; the rest are a straightforward continuation of what I've shown.

import Cocoa

//MARK:- AngleType    
protocol AngleType: FloatLiteralConvertible, IntegerLiteralConvertible {
    var value: Double { get set }

    init(_ value: Double)
    init(_ value: Int)
    init<T: IntegerType>(integerLiteral value: T)
    init<T: FloatingPointType>(floatLiteral value: T)
}


// Implement FloatLiteralConvertible and IntegerLiteralConvertible
extension AngleType {
    init<T: IntegerType>(integerLiteral value: T) {
        self.init(value)
    }

    init<T: IntegerType>(_ value: T) {
        self.init(integerLiteral: value)
    }

    init<T: FloatingPointType>(floatLiteral value: T) {
        self.init(value)
    }

    init<T: FloatingPointType>(_ value: T) {
        self.init(floatLiteral: value)
    }
}

//MARK:- Degree
struct Degree: AngleType {
    typealias FloatLiteralType = Double
    typealias IntegerLiteralType = Int

    var value: Double

    init(_ value: Double) {
        self.value = value
    }

    init(_ value: Int) {
        self.value = Double(value)
    }
}

protocol DegreeConvertible {
    init(degreeLiteral value: Degree)
}

extension Degree: CustomStringConvertible, CustomDebugStringConvertible {
    var description: String {
        return self.value.description
    }

    var debugDescription: String {
        return "\(self.value.description)°"
    }
}

extension Degree: RadianConvertible {
    init(radianLiteral value: Radian) {
        self.value = Double(radianLiteral:value) * 180.0 / M_PI
    }

    init(_ value: Radian) {
        self.init(radianLiteral: value)
    }
}

//MARK:- Radian
struct Radian: AngleType {
    typealias FloatLiteralType = Double
    typealias IntegerLiteralType = Int

    var value: Double

    init(_ value: Double) {
        self.value = value
    }

    init(_ value: Int) {
        self.value = Double(value)
    }
}

protocol RadianConvertible {
    init(radianLiteral value: Radian)
}

extension Radian: CustomStringConvertible, CustomDebugStringConvertible {
    var description: String {
        return self.value.description
    }

    var debugDescription: String {
        return "\(self.value.description)㎭"
    }
}

extension Radian: DegreeConvertible {
    init(degreeLiteral value: Degree) {
        self.value = Double(degreeLiteral: value) * M_PI / 180.0
    }

    init(_ value: Degree) {
        self.init(degreeLiteral: value)
    }
}

//MARK:- Adding Conformance To Built In Types
extension FloatLiteralType: DegreeConvertible, RadianConvertible {
    init(degreeLiteral degree: Degree) {
        self = degree.value
    }

    init(radianLiteral radian: Radian) {
        self = radian.value
    }
}

extension CGFloat: DegreeConvertible, RadianConvertible {
    init(degreeLiteral degree: Degree) {
        self.init(degree.value)
    }

    init(radianLiteral radian: Radian) {
        self.init(radian.value)
    }

    init(_ degree: Degree) {
        self.init(degreeLiteral: degree)
    }

    init(_ radian: Radian) {
        self.init(radianLiteral: radian)
    }
}

func sin(value: Radian) -> Double { return sin(Double(value.value)) }
func asin(value: Double) -> Radian { return Radian(Double(asin(value))) }
func cos(value: Radian) -> Double{ return cos(Double(value.value)) }
func acos(value: Double) -> Radian { return Radian(Double(acos(value))) }


func sin(value: Degree) -> Double{ return sin(Radian(value)) }
func asin(value: Double) -> Degree { return Degree(Double(asin(value))) }
func cos(value: Degree) -> Double{ return cos(Radian(value)) }
func acos(value: Double) -> Degree { return Degree(Double(acos(value))) }

let d180: Degree = Degree(180.0)
let r180: Radian = Radian(degreeLiteral: d180)

let d0 = Degree(0.0)
let r0 = Radian(d0)

let dsin180 = sin(d180)
let rsin180 = sin(r180)
let dcos180 = cos(d180)
let rcos180 = cos(r180)

let dsin0 = sin(d0)
let rsin0 = sin(r0)
let dcos0 = cos(d0)
let rcos0 = cos(r0)

let adsin180: Degree = asin(dsin180)
let adcos180: Degree = acos(dcos180)



回答5:


There's no setting or property to change the built-in trigonometric functions. You should just work strictly in radians if you want to simplify your expressions, or define your own sindeg, cosdeg, etc.

Each of the floating-point types has a built-in static member named pi whose value is the best approximation to π. For example: Double.pi, Float.pi, CGFloat.pi.

Also, sin 90˚ is 1, not 0.




回答6:


I'm not entirely sure why you'd want to overload the default global method, but if you must, you can provide an alternate method signature:

func sin(#degrees: Double) -> Double { // Require a parameter name for method call
    let radians: Double = degrees * (M_PI / 180) // Convert to rad
    return sin(radians) // Return result of default method call with automatic conversion
}

sin(degrees: 90) // 1.0
sin(degrees: 180) // 0.0

However, this is really an odd way of doing it, and it would make more sense to explicitly define your own method (that's what they're for), in a similar way:

func sinFromDegrees(degrees: Double) -> Double {
    let radians: Double = degrees * (M_PI / 180)
    return sin(radians)
}

sinFromDegrees(90) // 1.0
sinFromDegrees(180) // 0.0



回答7:


Seeing as I use trig a lot. I found the best way was to define some functions outside of the class ViewController.

If you define them in any one of your .swift files just below the imports and just above the class ViewController:UIViewController { } then you can call them throughout the whole project.

So for the sin function, I named it sindeg() standing for "sin degrees".

func sindeg(degrees: Double) -> Double {
    return sin(degrees * M_PI / 180.0)
    }

So this takes your degrees number converts it, solves it and returns as degrees. So all you need to do is type sindeg(45.5) and the result would = 0.71325045.

Here is the others:

func cosdeg(degrees: Double) -> Double {
    return cos(degrees * M_PI / 180.0)
}
func tandeg(degrees: Double) -> Double {
    return tan(degrees * M_PI / 180.0)
}

arcTan here is very similar, only difference is the return formula

 func atanDegree(degrees: Double) -> Double {
        return atan(degrees) * 180 / M_PI
    }

This one is just to convert a radian value to degrees. Takes in radians, converts, returns back degrees.

func Convert(radians: Double) -> Double {
    return radians * 180.0 / M_PI
}


来源:https://stackoverflow.com/questions/28598307/make-swift-assume-degrees-for-trigonometry-calculations

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!