/* --------------------------------------------------------- * * __________ D E L T A S C R I P T * * (_________() * * / === / - A fast, dynamic scripting language * * | == | - Version 4.13.11.0 * * / === / - Developed by Adam R. Nelson * * | = = | - 2011-2013 * * / === / - Distributed under GNU LGPL v3 * * (________() - http://github.com/ar-nelson/deltascript * * * * --------------------------------------------------------- */ package com.sector91.delta.script.objects; import static com.sector91.delta.script.NumberTypes.SHORT_FLOAT; import com.sector91.delta.script.DeltaScript; import com.sector91.delta.script.NumberTypes; import com.sector91.delta.script.annotations.DSDynamicField; import com.sector91.delta.script.annotations.DSInaccessible; import com.sector91.delta.script.annotations.DSName; import com.sector91.delta.script.annotations.DSType; import com.sector91.delta.script.objects.reflect.DS_JavaClass; /** * <p>A DeltaScript decimal or floating-point number. May be represented * internally by a {@code float}, {@code double}, or {@link BigDecimal}; see * {@link NumberTypes} for more information.</p> * * @author Adam R. Nelson * @version 4.13.11.0 */ @DSType("Decimal") public abstract class DS_Decimal extends DS_Scalar { private static final long serialVersionUID = DeltaScript.VERSION.majorVersion(); public static final String TYPE_NAME = "Decimal"; private static final DS_JavaClass DSCLASS = DS_JavaClass.fromClass( DS_Decimal.class); @DSInaccessible public static final float FLOAT_EPSILON = 0.000001f; private static final int MAX_ULPS = 5; /** * <p>Tests if two floating-point numbers are <em>approximately</em> equal * by comparing their binary representations. The least-significant bits of * the {@code float}s are compared, and they are considered "close * enough" if the difference is <= 5.</p> * * <p>Taken from <a href= * "http://www.cygnus-software.com/papers/comparingfloats/Comparing%20floating%20point%20numbers.htm" * >Bruce Dawson's page on floating-point comparisons.</a></p> * * @param a The first {@code float} to compare. * @param b The second {@code float} to compare. * @return True if the difference between the lowest bits of both * {@code float}s' binary representations is <= 5. * @since 3.12.2.0 */ @DSInaccessible public static boolean almostEqual(float a, float b) { int aInt = Float.floatToRawIntBits(a); // Make aInt lexicographically ordered as a twos-complement int if (aInt < 0) aInt = 0x80000000 - aInt; // Make bInt lexicographically ordered as a twos-complement int int bInt = Float.floatToRawIntBits(b); if (bInt < 0) bInt = 0x80000000 - bInt; int intDiff = Math.abs(aInt - bInt); if (intDiff <= MAX_ULPS) return true; return false; } // API Methods // ---------------------------------------------------- @DSName("sin") @DSDynamicField @Override public DS_Decimal sin() {return super.sin();} @DSName("cos") @DSDynamicField @Override public DS_Decimal cos() {return super.cos();} @DSName("tan") @DSDynamicField @Override public DS_Decimal tan() {return super.tan();} @DSName("asin") @DSDynamicField @Override public DS_Decimal asin() {return super.asin();} @DSName("acos") @DSDynamicField @Override public DS_Decimal acos() {return super.acos();} @DSName("atan") @DSDynamicField @Override public DS_Decimal atan() {return super.atan();} @DSName("ceil") @DSDynamicField @Override public abstract DS_Integer ceil(); @DSName("floor") @DSDynamicField @Override abstract public DS_Integer floor(); @DSName("round") @DSDynamicField @Override public abstract DS_Integer round(); @DSName("sqrt") @DSDynamicField @Override public abstract DS_Decimal sqrt(); /** * <p>Rounds this decimal to a specific number of decimal places. The value * of {@code places} may be negative, in which case a number of places to * the left of the decimal point will be rounded off (e.g., * {@code 123.4.roundTo(-1)} returns 120.0).</p> * * @param places The number of decimal places to round to. * @return A new decimal, equal to this decimal rounded to the given number * of decimal places. */ @DSName("roundTo") public DS_Scalar roundTo(int places) { final double exp = Math.pow(10f, places); return ScalarFactory.fromNumber(Math.round(doubleValue()*exp)/exp, getNumberType()); } /** * <p>Determines if this decimal is <em>close to</em> another decimal; that * is, within a reasonable margin of error that may occur from * floating-point error.</p> * * @param other The {@link DS_Scalar} to compare to this decimal. * @return Whether this decimal is within a reasonable margin of error from * the given number. * @see #closeTo(DS_Scalar) * @since 3.12.2.0 */ @DSName("closeTo") public boolean closeTo(DS_Scalar other) {return almostEqual(floatValue(), other.floatValue());} @Override public boolean equals(DS_Object other) { if (other instanceof DS_Scalar) { final DS_Scalar o = (DS_Scalar)other; final int cmpType = (getNumberType()&o.getNumberType())|SHORT_FLOAT; return testEquality(o, cmpType); } return false; } @DSInaccessible @Override public final boolean isIntegral() {return false;} @DSName("NaN") @DSDynamicField @Override public abstract boolean isNaN(); @DSName("infinite") @DSDynamicField @Override public abstract boolean isInfinite(); // DS_Object Methods // ---------------------------------------------------- public String getTypeName() {return TYPE_NAME;} @Override protected DS_JavaClass getDeltaScriptClass() {return DSCLASS;} }