使Swift假定三角函数计算中的角度为度数

6

有没有可能在Swift的iOS中更改设置、属性等,以便进行三角函数计算时使用度数而不是弧度?

例如sin(90)将计算为1

我有以下代码:

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

...但对于一些长的三角函数方程,转换变得非常复杂。


2
你可以创建新的函数,接受以度为单位的参数并将其转换为弧度,并从已定义的函数中返回结果。 - rakeshbs
1
但是对于一些长的三角方程,转换变得非常复杂。我同意@rakeshbs的观点 - 这就是为什么上帝赋予了函数,这样你就可以自动地进行任何转换,而不需要做任何“复杂”的事情。 - matt
4
是的,但上帝运作于弧度而非角度。 - rob mayoff
NSUnitAngle将在iOS 10中推出。https://developer.apple.com/reference/foundation/nsunitangle - markedwardmurray
7个回答

32

正如其他答案中已经提到的,标准库中没有以度为单位的三角函数。

如果您定义自己的函数,则可以使用__sinpi()__cospi()等函数,而不是乘以π:

// 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)
}

来自__sinpi手册页(加强语调):

__sinpi()函数返回x乘以π的正弦值(以弧度为单位)。由于它可以隐式地使用尽可能多的π位数,以提供一个结果更为精确和完整,所以相较于sin(M_PI * x),计算结果更准确。对于大的x值,这个函数也可以更有效率,因为涉及到的参数缩减显著简单。

__sinpi()和相关函数是非标准的,但在iOS 7 / OS X 10.9及以上版本可用。

示例:

sin(degrees: 180.0)       // 0

给出一个精确的结果,与之相反:

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

只是为了好玩:您可以使用函数重载(现已更新为 Swift 3)定义基于度数的正弦函数,适用于所有浮点类型,包括 CGFloat

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))
}
在最后一种变体中,编译器会根据 degrees.native 的实际类型自动推断调用哪个函数,因此可以在32位和64位平台上正确运行。

3
嗨@MartinR,“__sinpi”肯定是整个网站上最好的“专业技巧”之一。我作为感谢之意发送了一笔赏金。mfg - Fattie
2
这是一个非常棒且描述详细的答案。 - CuriousDev

7

添加一个扩展名以清晰地识别值的类型是处理这种情况的适当方式:

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 }
}

将其放入游乐场中,看看你能得到预期的结果:
sin(90.degrees)  --> 1.0
1.㎭  -->  57.2957795130823
1.㎭.degrees --> 1.0
(M_PI / 3).㎭  -->  60.0

3

这个程序在播放场中运行,并提供了度/弧度单位的类型安全实现。类型定义是从Swift进化邮件列表这里自由获取的,做了一些小的语法修正。我添加了一些三角函数;其余的是基于我所展示内容的简单延续。

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)

3
你可以定义全局函数来返回一个角度值的正弦值。只需将该函数放置在任何类之外的Swift文件中即可。
func sind(degrees: Double) -> Double {
    return sin(degrees * M_PI / 180.0)
}

所以无论在你的项目中的任何地方,都可以直接使用:

sind(90) // Returns 1

2
没有设置或属性可以改变内置三角函数。如果你想简化表达式,应该严格按照弧度制工作,或者定义自己的sindeg、cosdeg等函数。
每种浮点类型都有一个名为pi的内置静态成员,其值是π的最佳近似值。例如:Double.pi、Float.pi、CGFloat.pi。
另外,sin 90˚的值是1,而不是0。

1

我不完全确定为什么您想要重载默认的全局方法,但如果必须这样做,您可以提供替代的方法签名:

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

不过,这种做法确实有些奇怪,更合理的做法是明确定义自己的方法(这就是它们存在的原因),类似于以下方式:

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

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

1

由于我经常使用三角函数,我发现最好的方法是在ViewController类之外定义一些函数。

如果你在任何一个.swift文件中定义它们,只需要在imports下面和class ViewController:UIViewController { }上面就可以在整个项目中调用它们。

因此,对于sin函数,我将其命名为sindeg(),表示“sin度数”。

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

这个函数会将你输入的角度数转换并求解,然后以角度形式返回结果。所以你只需要输入 sindeg(45.5),结果将会是 0.71325045。

以下是其他函数:

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在这里非常相似,唯一的区别是返回公式。

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

这个函数是用来将弧度值转换为角度。输入弧度,进行转换,返回角度。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接