hints

package
v3.0.0-...-8217f41 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 1, 2018 License: MIT Imports: 0 Imported by: 0

Documentation

Overview

Hints: Chapter 1, Hints and Tips.

This is a documentation only package. Code suggested by this chapter is in the package base. The sections of the chapter are briefly discussed here.

Trigonometric functions of large angles: The function base.PMod reduces angles (or any floating point quantity) to a range from 0 to a given positive number. This satisfies the suggestion of this section, but see the Go examples for this function. Reducing the range of a number may or may not offer accuracy advantages.

Angle modes: Functions in the standard Go math library work in radians. To avoid inefficiencies of repeated conversions, all packages of this library work with angle and angle-like quantities in radians. Formulas in the book typically take degrees. These formulas are generally translated to radians so that as much work as possible can be done in radians. It is true that IO will often be done in degrees, but IO is not speed limiting. For computational reasons, interal formats, function arguments, and return values are almost always radians.

Right ascensions: The base package has the function FromSexa for ingesting sexagesimal quantities such as right ascensions, and it defines types for angles, hour angles, right ascensions, and times. Example 1.a of this chapter is implemented as a package example below.

The correct quadrant: Go has a complete set of inverse trigonometric functions, including math.Atan2.

The input of negative angles: The function base.FromSexa has a "neg" parameter to negate the overall quantity. The numeric components are generally passed as positive numbers. This avoids the problems described in the text.

Powers of time: Meeus offers some cautionary anecdotes here, but provides no concrete rules to follow, no algorithms for determining when periodic terms might be neglected. There is no code from this section.

Avoiding powers: The function base.Horner implements Horner's method, and is used heavily by other packages.

To shorten a program: Shortening a program is rarely a goal these days. Computer storage is huge and program or data size is almost never a limit.

Go's features for included data are not much like the DATA statement of BASIC. Generally packages of this library which include lists or tables of data simply encode them as statically typed literals. See deltat.table10A for an example of a table encoded as a literal. (Click on the function name Interp10A when viewing the documentation in a browser, and a source window opens. Scroll up slightly to see the table.)

Another possibility in Go is to encode data as "raw strings" (see http://golang.org/ref/spec#String_literals) and them parse them as needed. This technique can be useful if you have data in a text file which is provided to you from some external source. Pasting the text file as a large raw string can avoid transcription errors that might occur if you manually reformat data as typed literals. Consider though, the run-time cost of parsing the data. Even if it is done at program startup, it can lead to significant program startup times. Ultimately you might write a standalone utility program that reads the text file and emits a Go source file containing the data reformatted as literals.

Safety tests: There seems nothing unique to astronomy here. Write correct code, check all errors, handle all cases, code defensively. For those new to Go, read up on error handling philosophy of Go. We don't use assertions, we don't use exceptions as error handlers.

Debugging. Go has numerous features to avoid errors and help programmers write error free code. Exception conditions terminate the program and identify the site of the failure. If the bug is not obvious from examining the code at the site of failure, it then rarely takes more than a test-print or two to find a bug. For more traditional debugging, the compiled executables include data to enable debugging with popular debuggers. You can swap numbers with x, y = y, x.

Checking the results: See the go test feature.

Example (PMod_integer)

Section "Trigonometric functions of large angles":

Meeus makes his point, but an example with integer values is a bit unfair when trigonometric functions inherently work on floating point numbers.

package main

import (
	"fmt"
	"math"

	"github.com/soniakeys/unit"
)

func main() {
	const large = 36000030

	// The direct function call loses precition as expected.
	fmt.Println("Direct:    ", math.Sin(large*math.Pi/180))

	// When the value is manually reduced to the integer 30, the Go constant
	// evaluaton does a good job of delivering a radian value to math.Sin that
	// evaluates to .5 exactly.
	fmt.Println("Integer 30:", math.Sin(30*math.Pi/180))

	// Math.Mod takes float64s and returns float64s.  The integer constants
	// here however can be represented exactly as float64s, and the returned
	// result is exact as well.
	fmt.Println("Math.Mod:  ", math.Mod(large, 360))

	// But when math.Mod is substituted into the Sin function, float64s
	// are multiplied instead of the high precision constants, and the result
	// comes back slightly inexact.
	fmt.Println("Sin Mod:   ", math.Sin(math.Mod(large, 360)*math.Pi/180))

	// Use of unit.PMod on integer constants produces results identical to
	// above.
	fmt.Println("PMod int:  ", math.Sin(unit.PMod(large, 360)*math.Pi/180))

	// As soon as the large integer is scaled to a non-integer value though,
	// precision is lost and PMod is of no help recovering at this point.
	fmt.Println("PMod float:", math.Sin(unit.PMod(large*math.Pi/180, 2*math.Pi)))
}
Output:

Direct:     0.49999999995724154
Integer 30: 0.5
Math.Mod:   30
Sin Mod:    0.49999999999999994
PMod int:   0.49999999999999994
PMod float: 0.49999999997845307
Example (PMod_mars)

Section "Trigonometric functions of large angles":

A non integer example better illustrates that reduction to a range does not rescue precision.

package main

import (
	"fmt"
	"math"

	"github.com/soniakeys/unit"
)

func main() {
	// Angle W from step 9 of example 42.a, as suggested.
	const W = 5492522.4593
	const reduced = 2.4593

	// Direct function call.  It's a number.  How correct is it?
	fmt.Println("Direct:  ", math.Sin(W*math.Pi/180))

	// Manually reduced to range 0-360.  This is presumably the "correct"
	// answer, but note that the reduced number has a reduced number of
	// significant digits.  The answer cannot have any more significant digits.
	fmt.Println("Reduced: ", math.Sin(reduced*math.Pi/180))

	// Accordingly, PMod cannot rescue any precision, whether done on degrees
	// or radians.
	fmt.Println("PMod deg:", math.Sin(unit.PMod(W, 360)*math.Pi/180))
	fmt.Println("PMod rad:", math.Sin(unit.PMod(W*math.Pi/180, 2*math.Pi)))
}
Output:

Direct:   0.04290970350270464
Reduced:  0.04290970350923273
PMod deg: 0.04290970351307828
PMod rad: 0.04290970350643808
Example (RA)
package main

import (
	"fmt"

	"github.com/soniakeys/unit"
)

func main() {
	// Example 1.a, p. 8
	h := unit.FromSexa(' ', 9, 14, 55.8)
	fmt.Printf("%.9f\n", h)
	α := unit.RAFromHour(h)
	fmt.Printf("%.5f\n", α.Deg())
	fmt.Printf("%.6f\n", α.Tan())
}
Output:

9.248833333
138.73250
-0.877517

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL