/* --------------------------------------------------------- *
* __________ 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;}
}