|
| 1 | +/* |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more |
| 3 | + * contributor license agreements. See the NOTICE file distributed with |
| 4 | + * this work for additional information regarding copyright ownership. |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 |
| 6 | + * (the "License"); you may not use this file except in compliance with |
| 7 | + * the License. You may obtain a copy of the License at |
| 8 | + * |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | + * |
| 11 | + * Unless required by applicable law or agreed to in writing, software |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | + * See the License for the specific language governing permissions and |
| 15 | + * limitations under the License. |
| 16 | + */ |
| 17 | +package org.elasticsearch.common.util; |
| 18 | + |
| 19 | +/* |
| 20 | + * ============================================================================= |
| 21 | + * |
| 22 | + * sinh and atan ported from |
| 23 | + * https://github.com/yannrichet/jmathplot/blob/f25426e0ab0e68647ad2b75f577c7be050ecac86/src/main/java/org/math/plot/utils/FastMath.java |
| 24 | + * (Apache 2.0) Copyright 2012 Jeff Hain, which is partially derived from fdlibm package |
| 25 | + * |
| 26 | + * ============================================================================= |
| 27 | + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. |
| 28 | + * |
| 29 | + * Developed at SunSoft, a Sun Microsystems, Inc. business. |
| 30 | + * Permission to use, copy, modify, and distribute this |
| 31 | + * software is freely granted, provided that this notice |
| 32 | + * is preserved. |
| 33 | + * ============================================================================= |
| 34 | + */ |
| 35 | + |
| 36 | + |
| 37 | +/** |
| 38 | + * Similar to Lucene's SloppyMath, but for additional math functions. |
| 39 | + */ |
| 40 | +public class ESSloppyMath { |
| 41 | + |
| 42 | + private ESSloppyMath() {} |
| 43 | + |
| 44 | + //-------------------------------------------------------------------------- |
| 45 | + // RE-USABLE CONSTANTS |
| 46 | + //-------------------------------------------------------------------------- |
| 47 | + |
| 48 | + private static final double ONE_DIV_F2 = 1/2.0; |
| 49 | + private static final double ONE_DIV_F3 = 1/6.0; |
| 50 | + private static final double ONE_DIV_F4 = 1/24.0; |
| 51 | + private static final double TWO_POW_N28 = Double.longBitsToDouble(0x3E30000000000000L); |
| 52 | + private static final double TWO_POW_66 = Double.longBitsToDouble(0x4410000000000000L); |
| 53 | + private static final double LOG_DOUBLE_MAX_VALUE = StrictMath.log(Double.MAX_VALUE); |
| 54 | + |
| 55 | + //-------------------------------------------------------------------------- |
| 56 | + // CONSTANTS AND TABLES FOR ATAN |
| 57 | + //-------------------------------------------------------------------------- |
| 58 | + |
| 59 | + // We use the formula atan(-x) = -atan(x) |
| 60 | + // ---> we only have to compute atan(x) on [0,+infinity[. |
| 61 | + // For values corresponding to angles not close to +-PI/2, we use look-up tables; |
| 62 | + // for values corresponding to angles near +-PI/2, we use code derived from fdlibm. |
| 63 | + |
| 64 | + // Supposed to be >= tan(67.7deg), as fdlibm code is supposed to work with values > 2.4375. |
| 65 | + private static final double ATAN_MAX_VALUE_FOR_TABS = StrictMath.tan(Math.toRadians(74.0)); |
| 66 | + |
| 67 | + private static final int ATAN_TABS_SIZE = 1<<12 + 1; |
| 68 | + private static final double ATAN_DELTA = ATAN_MAX_VALUE_FOR_TABS/(ATAN_TABS_SIZE - 1); |
| 69 | + private static final double ATAN_INDEXER = 1/ATAN_DELTA; |
| 70 | + private static final double[] atanTab = new double[ATAN_TABS_SIZE]; |
| 71 | + private static final double[] atanDer1DivF1Tab = new double[ATAN_TABS_SIZE]; |
| 72 | + private static final double[] atanDer2DivF2Tab = new double[ATAN_TABS_SIZE]; |
| 73 | + private static final double[] atanDer3DivF3Tab = new double[ATAN_TABS_SIZE]; |
| 74 | + private static final double[] atanDer4DivF4Tab = new double[ATAN_TABS_SIZE]; |
| 75 | + |
| 76 | + private static final double ATAN_HI3 = Double.longBitsToDouble(0x3ff921fb54442d18L); // 1.57079632679489655800e+00 atan(inf)hi |
| 77 | + private static final double ATAN_LO3 = Double.longBitsToDouble(0x3c91a62633145c07L); // 6.12323399573676603587e-17 atan(inf)lo |
| 78 | + private static final double ATAN_AT0 = Double.longBitsToDouble(0x3fd555555555550dL); // 3.33333333333329318027e-01 |
| 79 | + private static final double ATAN_AT1 = Double.longBitsToDouble(0xbfc999999998ebc4L); // -1.99999999998764832476e-01 |
| 80 | + private static final double ATAN_AT2 = Double.longBitsToDouble(0x3fc24924920083ffL); // 1.42857142725034663711e-01 |
| 81 | + private static final double ATAN_AT3 = Double.longBitsToDouble(0xbfbc71c6fe231671L); // -1.11111104054623557880e-01 |
| 82 | + private static final double ATAN_AT4 = Double.longBitsToDouble(0x3fb745cdc54c206eL); // 9.09088713343650656196e-02 |
| 83 | + private static final double ATAN_AT5 = Double.longBitsToDouble(0xbfb3b0f2af749a6dL); // -7.69187620504482999495e-02 |
| 84 | + private static final double ATAN_AT6 = Double.longBitsToDouble(0x3fb10d66a0d03d51L); // 6.66107313738753120669e-02 |
| 85 | + private static final double ATAN_AT7 = Double.longBitsToDouble(0xbfadde2d52defd9aL); // -5.83357013379057348645e-02 |
| 86 | + private static final double ATAN_AT8 = Double.longBitsToDouble(0x3fa97b4b24760debL); // 4.97687799461593236017e-02 |
| 87 | + private static final double ATAN_AT9 = Double.longBitsToDouble(0xbfa2b4442c6a6c2fL); // -3.65315727442169155270e-02 |
| 88 | + private static final double ATAN_AT10 = Double.longBitsToDouble(0x3f90ad3ae322da11L); // 1.62858201153657823623e-02 |
| 89 | + |
| 90 | + static { |
| 91 | + // atan |
| 92 | + for (int i=0;i<ATAN_TABS_SIZE;i++) { |
| 93 | + // x: in [0,ATAN_MAX_VALUE_FOR_TABS]. |
| 94 | + double x = i * ATAN_DELTA; |
| 95 | + double onePlusXSqInv = 1.0/(1+x*x); |
| 96 | + double onePlusXSqInv2 = onePlusXSqInv*onePlusXSqInv; |
| 97 | + double onePlusXSqInv3 = onePlusXSqInv2*onePlusXSqInv; |
| 98 | + double onePlusXSqInv4 = onePlusXSqInv2*onePlusXSqInv2; |
| 99 | + atanTab[i] = StrictMath.atan(x); |
| 100 | + atanDer1DivF1Tab[i] = onePlusXSqInv; |
| 101 | + atanDer2DivF2Tab[i] = (-2*x*onePlusXSqInv2) * ONE_DIV_F2; |
| 102 | + atanDer3DivF3Tab[i] = ((-2+6*x*x)*onePlusXSqInv3) * ONE_DIV_F3; |
| 103 | + atanDer4DivF4Tab[i] = ((24*x*(1-x*x))*onePlusXSqInv4) * ONE_DIV_F4; |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + /** |
| 108 | + * A faster and less accurate {@link Math#sinh} |
| 109 | + * |
| 110 | + * @param value A double value. |
| 111 | + * @return Value hyperbolic sine. |
| 112 | + */ |
| 113 | + public static double sinh(double value) { |
| 114 | + // sinh(x) = (exp(x)-exp(-x))/2 |
| 115 | + double h; |
| 116 | + if (value < 0.0) { |
| 117 | + value = -value; |
| 118 | + h = -0.5; |
| 119 | + } else { |
| 120 | + h = 0.5; |
| 121 | + } |
| 122 | + if (value < 22.0) { |
| 123 | + if (value < TWO_POW_N28) { |
| 124 | + return (h < 0.0) ? -value : value; |
| 125 | + } else { |
| 126 | + double t = Math.expm1(value); |
| 127 | + // Might be more accurate, if value < 1: return h*((t+t)-t*t/(t+1.0)). |
| 128 | + return h * (t + t/(t+1.0)); |
| 129 | + } |
| 130 | + } else if (value < LOG_DOUBLE_MAX_VALUE) { |
| 131 | + return h * Math.exp(value); |
| 132 | + } else { |
| 133 | + double t = Math.exp(value*0.5); |
| 134 | + return (h*t)*t; |
| 135 | + } |
| 136 | + } |
| 137 | + |
| 138 | + /** |
| 139 | + * A faster and less accurate {@link Math#atan} |
| 140 | + * |
| 141 | + * @param value A double value. |
| 142 | + * @return Value arctangent, in radians, in [-PI/2,PI/2]. |
| 143 | + */ |
| 144 | + public static double atan(double value) { |
| 145 | + boolean negateResult; |
| 146 | + if (value < 0.0) { |
| 147 | + value = -value; |
| 148 | + negateResult = true; |
| 149 | + } else { |
| 150 | + negateResult = false; |
| 151 | + } |
| 152 | + if (value == 1.0) { |
| 153 | + // We want "exact" result for 1.0. |
| 154 | + return negateResult ? -Math.PI/4 : Math.PI/4; |
| 155 | + } else if (value <= ATAN_MAX_VALUE_FOR_TABS) { |
| 156 | + int index = (int)(value * ATAN_INDEXER + 0.5); |
| 157 | + double delta = value - index * ATAN_DELTA; |
| 158 | + double result = atanTab[index] + delta * (atanDer1DivF1Tab[index] + delta * (atanDer2DivF2Tab[index] |
| 159 | + + delta * (atanDer3DivF3Tab[index] + delta * atanDer4DivF4Tab[index]))); |
| 160 | + return negateResult ? -result : result; |
| 161 | + } else { // value > ATAN_MAX_VALUE_FOR_TABS, or value is NaN |
| 162 | + // This part is derived from fdlibm. |
| 163 | + if (value < TWO_POW_66) { |
| 164 | + double x = -1/value; |
| 165 | + double x2 = x*x; |
| 166 | + double x4 = x2*x2; |
| 167 | + double s1 = x2*(ATAN_AT0+x4*(ATAN_AT2+x4*(ATAN_AT4+x4*(ATAN_AT6+x4*(ATAN_AT8+x4*ATAN_AT10))))); |
| 168 | + double s2 = x4*(ATAN_AT1+x4*(ATAN_AT3+x4*(ATAN_AT5+x4*(ATAN_AT7+x4*ATAN_AT9)))); |
| 169 | + double result = ATAN_HI3-((x*(s1+s2)-ATAN_LO3)-x); |
| 170 | + return negateResult ? -result : result; |
| 171 | + } else { // value >= 2^66, or value is NaN |
| 172 | + if (Double.isNaN(value)) { |
| 173 | + return Double.NaN; |
| 174 | + } else { |
| 175 | + return negateResult ? -Math.PI/2 : Math.PI/2; |
| 176 | + } |
| 177 | + } |
| 178 | + } |
| 179 | + } |
| 180 | +} |
0 commit comments