package org.opensha2.util; import java.math.BigDecimal; import java.math.RoundingMode; /** * Miscellaneous math utilities. In some cases, these methods obviate the need * for 3<sup>rd</sup> party imports. * * @author Peter Powers */ public final class Maths { private Maths() {} /** Constant for π/2. */ public static final double PI_BY_2 = Math.PI / 2; /** Constant for 2π. */ public static final double TWOPI = 2 * Math.PI; /** Conversion multiplier for degrees to radians */ public static final double TO_RAD = Math.toRadians(1.0); /** Conversion multiplier for radians to degrees */ public static final double TO_DEG = Math.toDegrees(1.0); /** * The precomputed √<span style="border-top:1px solid; padding:0 0.1em;" * >2</span>. */ public static final double SQRT_2 = Math.sqrt(2); /** * The precomputed √<span style="border-top:1px solid; padding:0 0.1em;" * >2π</span>. */ public static final double SQRT_2PI = Math.sqrt(2 * Math.PI); /** * Standardized normal variate {@code ε = (x - μ) / σ}. * * @param μ mean * @param σ standard deviation */ public static double epsilon(double μ, double σ, double x) { return (x - μ) / σ; } /** * Error function approximation of Abramowitz and Stegun, formula 7.1.26 in * the <em>Handbook of Mathematical Functions with Formulas, Graphs, and * Mathematical Tables</em>. Although the approximation is only valid for * {@code x ≥ 0}, because {@code erf(x)} is an odd function, * {@code erf(x) = −erf(−x)} and negative values are supported. */ public static double erf(double x) { return x < 0.0 ? -erfBase(-x) : erfBase(x); } private static final double P = 0.3275911; private static final double A1 = 0.254829592; private static final double A2 = -0.284496736; private static final double A3 = 1.421413741; private static final double A4 = -1.453152027; private static final double A5 = 1.061405429; private static double erfBase(double x) { double t = 1 / (1 + P * x); double tsq = t * t; return 1 - (A1 * t + A2 * tsq + A3 * tsq * t + A4 * tsq * tsq + A5 * tsq * tsq * t) * Math.exp(-x * x); } /** * Same as {@link Math#hypot(double, double)} without regard to intermediate * under/over flow. * * @param v1 first value * @param v2 second value * @see Math#hypot(double, double) */ public static double hypot(double v1, double v2) { return Math.sqrt(v1 * v1 + v2 * v2); } /** * Normal complementary cumulative distribution function. * * @param μ mean * @param σ standard deviation * @param x variate */ public static double normalCcdf(double μ, double σ, double x) { return (1.0 + erf((μ - x) / (σ * SQRT_2))) * 0.5; } /** * Normal probability density function. * * @param μ mean * @param σ standard deviation * @param x variate */ public static double normalPdf(double μ, double σ, double x) { return Math.exp((μ - x) * (x - μ) / (2 * σ * σ)) / (σ * SQRT_2PI); } /** * Step function for which {@code f(x) = }{ * {@code 1 if x ≤ μ; 0 if x > μ }}. * * @param μ mean * @param x variate */ public static double stepFunction(double μ, double x) { return x < μ ? 1.0 : 0.0; } /** * Round a double to a specified number of decimal places according to * {@link RoundingMode#HALF_UP}. Internally this method uses the scaling and * rounding capabilities of {@link BigDecimal}. Note that a negative * {@code scale} will round {@code value} to the specified number of places * above the decimal. * * @param value to round * @param scale the number of decimal places in the result */ public static double round(double value, int scale) { return round(value, scale, RoundingMode.HALF_UP); } /** * Round a double to a specified number of decimal places according to the * supplied {@link RoundingMode}. Internally this method uses the scaling and * rounding capabilities of {@link BigDecimal}. Note that a negative * {@code scale} will round {@code value} to the specified number of places * above the decimal. * * @param value to round * @param scale the number of decimal places in the result */ public static double round(double value, int scale, RoundingMode mode) { return BigDecimal.valueOf(value).setScale(scale, mode).doubleValue(); } }