Make Swift Assume Degrees for Trigonometry Calculations

Trigonometric functions in Swift

The problem with sine and cosine functions is that M_PI is an irrational number is approximately defined as 3.14159265358979323846264338327950288 which means that it has some error.

One possible solutions to your problem is having the ranges of input form -PI/2 to PI/2. This reduces the error of approximation. The following changes your range to -90 to 90 degrees.

sin(((fmod($0, 360) > 270 ? fmod($0, 360) - 270 : ((fmod($0, 360) > 90) ? 180 - fmod($0, 360) : fmod($0, 360))) * M_PI / 180.00)) }

Reference from here

Trigonometry Function (cos) Returning Different Value to a Calculator

The Darwin trig functions work in Radians.

Issue 1.1

print(cos(0))               // receiver error "Ambiguous use of 'cos'"
print(cos(90)) // receiver error "Ambiguous use of 'cos'"
print(cos(180)) // receiver error "Ambiguous use of 'cos'"
print(cos(270)) // receiver error "Ambiguous use of 'cos'"

These are ambiguous because there are two version of cos, which these two types:

  • (Double) -> Double
  • (Float) -> Float

Both Double and Float conform to ExpressibleByIntegerLiteral, so both can be expressed by an Int. It's ambiguous as to which of the two types the Int should express.

If you want the Double version, you can call it with:

  • cos(0 as Double)
  • cos(0.0) because direct use of a floatliteral as a Double takes precedence over a conversion via expressiblebyfloatliteral.

If you want the Float version, you can call it with: cos(0 as Float)

In both cases, the ambiguity can be resolved if context provides sufficient information. For example:

functionThatTakesADouble(cos(0)) // will use the `(Double) -> Double` version
functionThatTakesAFloat(cos(0)) // will use the `(Float) -> Float` version
print(cos(M_PI*0)) // M_PI is a Double, so 0 is treated as a Double

Issue 1.2

After resolving the type ambiguity, like so:

print(cos(0.0))   // 1.0
print(cos(90.0)) // -0.44807361612917
print(cos(180.0)) // -0.598460069057858
print(cos(270.0)) // 0.984381950632505

We get correct answers. Remember, the system trig functions work in radians.

Issue 2

print(cos(M_PI*0))          // returns 1
print(cos(M_PI*1)) // returns -1
print(cos(M_PI*2)) // returns 1
print(cos(M_PI*3)) // returns -1

These are all correct values.

Issue 3

print(cos(M_PI*0))          // returns 1
print(cos(M_PI*90)) // returns 1
print(cos(M_PI*180)) // returns 1
print(cos(M_PI*270)) // returns 1

These are also correct. cos is 1 for every even multiple of pi radians.

Issue 4

print(cos(M_PI/M_PI-1))     // returns 1
print(cos(M_PI/M_PI+89)) // returns -0.44
print(cos(M_PI/M_PI+179)) // returns -0.59
print(cos(M_PI/M_PI+269)) // reutnrs 0.98

M_PI/M_PI is 1.0 (as a Double), so these cases are equivalent to the first, but with the extra type information necessary to allow the compiler to unambiguously choose (Double) -> Double cos over (Float) -> Float.

How can I convert from degrees to radians?

Xcode 11 • Swift 5.1 or later

extension BinaryInteger {
var degreesToRadians: CGFloat { CGFloat(self) * .pi / 180 }
}

extension FloatingPoint {
var degreesToRadians: Self { self * .pi / 180 }
var radiansToDegrees: Self { self * 180 / .pi }
}

Playground

45.degreesToRadians         // 0.7853981633974483

Int(45).degreesToRadians // 0.7853981633974483
Int8(45).degreesToRadians // 0.7853981633974483
Int16(45).degreesToRadians // 0.7853981633974483
Int32(45).degreesToRadians // 0.7853981633974483
Int64(45).degreesToRadians // 0.7853981633974483

UInt(45).degreesToRadians // 0.7853981633974483
UInt8(45).degreesToRadians // 0.7853981633974483
UInt16(45).degreesToRadians // 0.7853981633974483
UInt32(45).degreesToRadians // 0.7853981633974483
UInt64(45).degreesToRadians // 0.7853981633974483

Double(45).degreesToRadians // 0.7853981633974483
CGFloat(45).degreesToRadians // 0.7853981633974483
Float(45).degreesToRadians // 0.7853981
Float80(45).degreesToRadians // 0.78539816339744830963

If you would like to make the binary integer return a floating point type instead of always returning a CGFloat you can make a generic method instead of a computed property:

extension BinaryInteger {
func degreesToRadians<F: FloatingPoint>() -> F { F(self) * .pi / 180 }
}

let radiansDouble: Double = 45.degreesToRadians()   // 0.7853981633974483
let radiansCGFloat: CGFloat = 45.degreesToRadians() // 0.7853981633974483
let radiansFloat: Float = 45.degreesToRadians() // 0.7853981
let radiansFloat80: Float80 = 45.degreesToRadians() // 0.78539816339744830963

Swift calculating a new point x distance in y direction

Here are a few clues:

  1. sin() takes radians not degrees, so you need to convert.
  2. 0 is right, not up so you need to adjust for that.
  3. use cos() for horizontal.

This is closer to what you need:

public func offset(byDistance distance:CGFloat, inDirection degrees:CGFloat) {
let radians = (degrees - 90) * .pi / 180
let vertical = sin(radians) * distance
let horizontal = cos(radians) * distance
self.position = self.position.applying(CGAffineTransform(translationX:horizontal, y:vertical))
}

Math function returns wrong value

The calculation is correct. However sin and cos take their param in radians, not degrees.

In calculus and most other branches of mathematics beyond practical
geometry, angles are universally measured in radians. One radian is
equal to 180/π degrees.

To convert from radians to degrees, multiply by 180/π.

https://en.wikipedia.org/wiki/Radian

RPN calculator (Swift xcode) - sine, cosine, tangent, reciprocal (1/x), log (base) functions

Here is an implementation of those basic functions in Swift. I'll let you put that into your RPN calculator, since that is your assignment.

The built-in trig functions need input in radians, so you have to convert by multiplying by pi and dividing by 180.0.

func sine(degrees: Double) -> Double {
return sin(degrees * M_PI / 180)
}

func cosine(degrees: Double) -> Double {
return cos(degrees * M_PI / 180)
}

func tangent(degrees: Double) -> Double {
return tan(degrees * M_PI / 180)
}

func log(n: Double, base: Double) -> Double {
return log(n) / log(base)
}

func reciprocal(n: Double) -> Double {
return 1.0 / n
}

For log(base 10) just use the built-in log10(n: Double) -> Double



Related Topics



Leave a reply



Submit