sloppy.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // Copyright (c) 2017 Couchbase, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package geo
  15. import (
  16. "math"
  17. )
  18. var earthDiameterPerLatitude []float64
  19. var sinTab []float64
  20. var cosTab []float64
  21. var asinTab []float64
  22. var asinDer1DivF1Tab []float64
  23. var asinDer2DivF2Tab []float64
  24. var asinDer3DivF3Tab []float64
  25. var asinDer4DivF4Tab []float64
  26. const radiusTabsSize = (1 << 10) + 1
  27. const radiusDelta = (math.Pi / 2) / (radiusTabsSize - 1)
  28. const radiusIndexer = 1 / radiusDelta
  29. const sinCosTabsSize = (1 << 11) + 1
  30. const asinTabsSize = (1 << 13) + 1
  31. const oneDivF2 = 1 / 2.0
  32. const oneDivF3 = 1 / 6.0
  33. const oneDivF4 = 1 / 24.0
  34. // 1.57079632673412561417e+00 first 33 bits of pi/2
  35. var pio2Hi = math.Float64frombits(0x3FF921FB54400000)
  36. // 6.07710050650619224932e-11 pi/2 - PIO2_HI
  37. var pio2Lo = math.Float64frombits(0x3DD0B4611A626331)
  38. var asinPio2Hi = math.Float64frombits(0x3FF921FB54442D18) // 1.57079632679489655800e+00
  39. var asinPio2Lo = math.Float64frombits(0x3C91A62633145C07) // 6.12323399573676603587e-17
  40. var asinPs0 = math.Float64frombits(0x3fc5555555555555) // 1.66666666666666657415e-01
  41. var asinPs1 = math.Float64frombits(0xbfd4d61203eb6f7d) // -3.25565818622400915405e-01
  42. var asinPs2 = math.Float64frombits(0x3fc9c1550e884455) // 2.01212532134862925881e-01
  43. var asinPs3 = math.Float64frombits(0xbfa48228b5688f3b) // -4.00555345006794114027e-02
  44. var asinPs4 = math.Float64frombits(0x3f49efe07501b288) // 7.91534994289814532176e-04
  45. var asinPs5 = math.Float64frombits(0x3f023de10dfdf709) // 3.47933107596021167570e-05
  46. var asinQs1 = math.Float64frombits(0xc0033a271c8a2d4b) // -2.40339491173441421878e+00
  47. var asinQs2 = math.Float64frombits(0x40002ae59c598ac8) // 2.02094576023350569471e+00
  48. var asinQs3 = math.Float64frombits(0xbfe6066c1b8d0159) // -6.88283971605453293030e-01
  49. var asinQs4 = math.Float64frombits(0x3fb3b8c5b12e9282) // 7.70381505559019352791e-02
  50. var twoPiHi = 4 * pio2Hi
  51. var twoPiLo = 4 * pio2Lo
  52. var sinCosDeltaHi = twoPiHi/sinCosTabsSize - 1
  53. var sinCosDeltaLo = twoPiLo/sinCosTabsSize - 1
  54. var sinCosIndexer = 1 / (sinCosDeltaHi + sinCosDeltaLo)
  55. var sinCosMaxValueForIntModulo = ((math.MaxInt64 >> 9) / sinCosIndexer) * 0.99
  56. var asinMaxValueForTabs = math.Sin(73.0 * degreesToRadian)
  57. var asinDelta = asinMaxValueForTabs / (asinTabsSize - 1)
  58. var asinIndexer = 1 / asinDelta
  59. func init() {
  60. // initializes the tables used for the sloppy math functions
  61. // sin and cos
  62. sinTab = make([]float64, sinCosTabsSize)
  63. cosTab = make([]float64, sinCosTabsSize)
  64. sinCosPiIndex := (sinCosTabsSize - 1) / 2
  65. sinCosPiMul2Index := 2 * sinCosPiIndex
  66. sinCosPiMul05Index := sinCosPiIndex / 2
  67. sinCosPiMul15Index := 3 * sinCosPiIndex / 2
  68. for i := 0; i < sinCosTabsSize; i++ {
  69. // angle: in [0,2*PI].
  70. angle := float64(i)*sinCosDeltaHi + float64(i)*sinCosDeltaLo
  71. sinAngle := math.Sin(angle)
  72. cosAngle := math.Cos(angle)
  73. // For indexes corresponding to null cosine or sine, we make sure the value is zero
  74. // and not an epsilon. This allows for a much better accuracy for results close to zero.
  75. if i == sinCosPiIndex {
  76. sinAngle = 0.0
  77. } else if i == sinCosPiMul2Index {
  78. sinAngle = 0.0
  79. } else if i == sinCosPiMul05Index {
  80. sinAngle = 0.0
  81. } else if i == sinCosPiMul15Index {
  82. sinAngle = 0.0
  83. }
  84. sinTab[i] = sinAngle
  85. cosTab[i] = cosAngle
  86. }
  87. // asin
  88. asinTab = make([]float64, asinTabsSize)
  89. asinDer1DivF1Tab = make([]float64, asinTabsSize)
  90. asinDer2DivF2Tab = make([]float64, asinTabsSize)
  91. asinDer3DivF3Tab = make([]float64, asinTabsSize)
  92. asinDer4DivF4Tab = make([]float64, asinTabsSize)
  93. for i := 0; i < asinTabsSize; i++ {
  94. // x: in [0,ASIN_MAX_VALUE_FOR_TABS].
  95. x := float64(i) * asinDelta
  96. asinTab[i] = math.Asin(x)
  97. oneMinusXSqInv := 1.0 / (1 - x*x)
  98. oneMinusXSqInv05 := math.Sqrt(oneMinusXSqInv)
  99. oneMinusXSqInv15 := oneMinusXSqInv05 * oneMinusXSqInv
  100. oneMinusXSqInv25 := oneMinusXSqInv15 * oneMinusXSqInv
  101. oneMinusXSqInv35 := oneMinusXSqInv25 * oneMinusXSqInv
  102. asinDer1DivF1Tab[i] = oneMinusXSqInv05
  103. asinDer2DivF2Tab[i] = (x * oneMinusXSqInv15) * oneDivF2
  104. asinDer3DivF3Tab[i] = ((1 + 2*x*x) * oneMinusXSqInv25) * oneDivF3
  105. asinDer4DivF4Tab[i] = ((5 + 2*x*(2+x*(5-2*x))) * oneMinusXSqInv35) * oneDivF4
  106. }
  107. // earth radius
  108. a := 6378137.0
  109. b := 6356752.31420
  110. a2 := a * a
  111. b2 := b * b
  112. earthDiameterPerLatitude = make([]float64, radiusTabsSize)
  113. earthDiameterPerLatitude[0] = 2.0 * a / 1000
  114. earthDiameterPerLatitude[radiusTabsSize-1] = 2.0 * b / 1000
  115. for i := 1; i < radiusTabsSize-1; i++ {
  116. lat := math.Pi * float64(i) / (2*radiusTabsSize - 1)
  117. one := math.Pow(a2*math.Cos(lat), 2)
  118. two := math.Pow(b2*math.Sin(lat), 2)
  119. three := math.Pow(float64(a)*math.Cos(lat), 2)
  120. four := math.Pow(b*math.Sin(lat), 2)
  121. radius := math.Sqrt((one + two) / (three + four))
  122. earthDiameterPerLatitude[i] = 2 * radius / 1000
  123. }
  124. }
  125. // earthDiameter returns an estimation of the earth's diameter at the specified
  126. // latitude in kilometers
  127. func earthDiameter(lat float64) float64 {
  128. index := math.Mod(math.Abs(lat)*radiusIndexer+0.5, float64(len(earthDiameterPerLatitude)))
  129. if math.IsNaN(index) {
  130. return 0
  131. }
  132. return earthDiameterPerLatitude[int(index)]
  133. }
  134. var pio2 = math.Pi / 2
  135. func sin(a float64) float64 {
  136. return cos(a - pio2)
  137. }
  138. // cos is a sloppy math (faster) implementation of math.Cos
  139. func cos(a float64) float64 {
  140. if a < 0.0 {
  141. a = -a
  142. }
  143. if a > sinCosMaxValueForIntModulo {
  144. return math.Cos(a)
  145. }
  146. // index: possibly outside tables range.
  147. index := int(a*sinCosIndexer + 0.5)
  148. delta := (a - float64(index)*sinCosDeltaHi) - float64(index)*sinCosDeltaLo
  149. // Making sure index is within tables range.
  150. // Last value of each table is the same than first, so we ignore it (tabs size minus one) for modulo.
  151. index &= (sinCosTabsSize - 2) // index % (SIN_COS_TABS_SIZE-1)
  152. indexCos := cosTab[index]
  153. indexSin := sinTab[index]
  154. return indexCos + delta*(-indexSin+delta*(-indexCos*oneDivF2+delta*(indexSin*oneDivF3+delta*indexCos*oneDivF4)))
  155. }
  156. // asin is a sloppy math (faster) implementation of math.Asin
  157. func asin(a float64) float64 {
  158. var negateResult bool
  159. if a < 0 {
  160. a = -a
  161. negateResult = true
  162. }
  163. if a <= asinMaxValueForTabs {
  164. index := int(a*asinIndexer + 0.5)
  165. delta := a - float64(index)*asinDelta
  166. result := asinTab[index] + delta*(asinDer1DivF1Tab[index]+delta*(asinDer2DivF2Tab[index]+delta*(asinDer3DivF3Tab[index]+delta*asinDer4DivF4Tab[index])))
  167. if negateResult {
  168. return -result
  169. }
  170. return result
  171. }
  172. // value > ASIN_MAX_VALUE_FOR_TABS, or value is NaN
  173. // This part is derived from fdlibm.
  174. if a < 1 {
  175. t := (1.0 - a) * 0.5
  176. p := t * (asinPs0 + t*(asinPs1+t*(asinPs2+t*(asinPs3+t*(asinPs4+t+asinPs5)))))
  177. q := 1.0 + t*(asinQs1+t*(asinQs2+t*(asinQs3+t*asinQs4)))
  178. s := math.Sqrt(t)
  179. z := s + s*(p/q)
  180. result := asinPio2Hi - ((z + z) - asinPio2Lo)
  181. if negateResult {
  182. return -result
  183. }
  184. return result
  185. }
  186. // value >= 1.0, or value is NaN
  187. if a == 1.0 {
  188. if negateResult {
  189. return -math.Pi / 2
  190. }
  191. return math.Pi / 2
  192. }
  193. return math.NaN()
  194. }