package client.net.sf.saxon.ce.value;
import client.net.sf.saxon.ce.om.StandardNames;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.tree.util.FastStringBuffer;
import client.net.sf.saxon.ce.type.*;
import java.math.BigDecimal;
/**
* A numeric (single precision floating point) value
*/
public final class FloatValue extends NumericValue {
public static final FloatValue ZERO = new FloatValue((float)0.0);
public static final FloatValue NEGATIVE_ZERO = new FloatValue((float)-0.0);
public static final FloatValue ONE = new FloatValue((float)1.0);
public static final FloatValue NaN = new FloatValue(Float.NaN);
private float value;
/**
* Constructor supplying a float
* @param value the value of the float
*/
public FloatValue(float value) {
this.value = value;
typeLabel = BuiltInAtomicType.FLOAT;
}
/**
* Constructor supplying a float and an AtomicType, for creating
* a value that belongs to a user-defined subtype of xs:float. It is
* the caller's responsibility to ensure that the supplied value conforms
* to the supplied type.
* @param value the value of the NumericValue
* @param type the type of the value. This must be a subtype of xs:float, and the
* value must conform to this type. The method does not check these conditions.
*/
public FloatValue(float value, AtomicType type) {
this.value = value;
typeLabel = type;
}
/**
* Determine the primitive type of the value. This delivers the same answer as
* getItemType().getPrimitiveItemType(). The primitive types are
* the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
* and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
*/
public BuiltInAtomicType getPrimitiveType() {
return BuiltInAtomicType.FLOAT;
}
/**
* Get the value
*/
public float getFloatValue() {
return value;
}
public double getDoubleValue() {
return (double)value;
}
/**
* Get the hashCode. This must conform to the rules for other NumericValue hashcodes
* @see NumericValue#hashCode
*/
public int hashCode() {
if (value > Integer.MIN_VALUE && value < Integer.MAX_VALUE) {
return (int)value;
} else {
return new Double(getDoubleValue()).hashCode();
}
}
/**
* Test whether the value is the double/float value NaN
*/
public boolean isNaN() {
return Float.isNaN(value);
}
/**
* Get the effective boolean value
* @return true unless the value is zero or NaN
*/
public boolean effectiveBooleanValue() {
return (value!=0.0 && !Float.isNaN(value));
}
/**
* Convert to target data type
* @param requiredType an integer identifying the required atomic type
* @return an AtomicValue, a value of the required type; or an ErrorValue
*/
public ConversionResult convertPrimitive(BuiltInAtomicType requiredType, boolean validate) {
switch(requiredType.getFingerprint()) {
case StandardNames.XS_BOOLEAN:
return BooleanValue.get(value!=0.0 && !Float.isNaN(value));
case StandardNames.XS_FLOAT:
case StandardNames.XS_NUMERIC:
case StandardNames.XS_ANY_ATOMIC_TYPE:
return this;
case StandardNames.XS_INTEGER:
if (Float.isNaN(value)) {
ValidationFailure err = new ValidationFailure("Cannot convert float NaN to an integer");
err.setErrorCode("FOCA0002");
return err;
}
if (Float.isInfinite(value)) {
ValidationFailure err = new ValidationFailure("Cannot convert float infinity to an integer");
err.setErrorCode("FOCA0002");
return err;
}
return IntegerValue.decimalToInteger(new BigDecimal(value));
case StandardNames.XS_DECIMAL:
try {
return new DecimalValue(value);
} catch (XPathException e) {
return new ValidationFailure(e);
}
case StandardNames.XS_DOUBLE:
return new DoubleValue((double)value);
case StandardNames.XS_STRING:
return new StringValue(getStringValueCS());
case StandardNames.XS_UNTYPED_ATOMIC:
return new UntypedAtomicValue(getStringValueCS());
default:
ValidationFailure err = new ValidationFailure("Cannot convert float to " +
requiredType.getDisplayName());
err.setErrorCode("XPTY0004");
return err;
}
}
/**
* Get the value as a String
* @return a String representation of the value
*/
/**
* Convert the double to a string according to the XPath 2.0 rules
* @return the string value
*/
public CharSequence getPrimitiveStringValue() {
// Same code as for DoubleValue, but using the Float type
if (Float.isNaN(value)) {
return "NaN";
} else if (Float.isInfinite(value)) {
return (value > 0 ? "INF" : "-INF");
}
if (isWholeNumber()) {
// TODO: negative zero
return ""+(long)value;
} else {
double a = Math.abs(value);
if (a < 1e6) {
if (a >= 1e-3) {
return Float.toString(value);
} else if (a >= 1e-6) {
return BigDecimal.valueOf(value).toPlainString();
} else {
BigDecimal dec = BigDecimal.valueOf(value);
return dec.toString();
}
} else if (a < 1e7) {
FastStringBuffer sb = new FastStringBuffer(Float.toString(value * 10));
sb.setCharAt(sb.length()-1, (char)((int)sb.charAt(sb.length()-1) - 1));
return sb;
} else {
return Float.toString(value);
}
}
}
/*// previously used DoubleValue
public CharSequence getPrimitiveStringValue() {
return new DoubleValue(value).getPrimitiveStringValue();
}
*/
/**
* Negate the value
*/
public NumericValue negate() {
return new FloatValue(-value);
}
/**
* Implement the XPath floor() function
*/
public NumericValue floor() {
return new FloatValue((float)Math.floor(value));
}
/**
* Implement the XPath ceiling() function
*/
public NumericValue ceiling() {
return new FloatValue((float)Math.ceil(value));
}
/**
* Implement the XPath round() function
*/
public NumericValue round() {
if (Float.isNaN(value)) {
return this;
}
if (Float.isInfinite(value)) {
return this;
}
if (value==0.0) {
return this; // handles the negative zero case
}
if (value >= -0.5 && value < 0.0) {
return new FloatValue((float)-0.0);
}
if (value > Integer.MIN_VALUE && value < Integer.MAX_VALUE) {
return new FloatValue((float)Math.round(value));
}
// if the float is larger than the maximum int, then
// it can't have any significant digits after the decimal
// point, so return it unchanged
return this;
}
/**
* Implement the XPath round-to-half-even() function
*/
public NumericValue roundHalfToEven(int scale) {
try {
return (FloatValue)
new DoubleValue((double)value).roundHalfToEven(scale).convertPrimitive(BuiltInAtomicType.FLOAT, true).asAtomic();
} catch (XPathException err) {
throw new AssertionError(err);
}
}
/**
* Determine whether the value is negative, zero, or positive
* @return -1 if negative, 0 if zero (including negative zero), +1 if positive, NaN if NaN
*/
public double signum() {
if (Float.isNaN(value)) {
return value;
}
if (value > 0) return 1;
if (value == 0) return 0;
return -1;
}
/**
* Determine whether the value is a whole number, that is, whether it compares
* equal to some integer
*/
public boolean isWholeNumber() {
return value == Math.floor(value) && !Float.isInfinite(value);
}
/**
* Get the absolute value as defined by the XPath abs() function
* @return the absolute value
* @since 9.2
*/
public NumericValue abs() {
if (value > 0.0) {
return this;
} else {
return new FloatValue(Math.abs(value));
}
}
public int compareTo(Object other) {
if (!(other instanceof NumericValue)) {
throw new ClassCastException("Numeric values are not comparable to " + other.getClass());
}
if (other instanceof FloatValue) {
float otherFloat = ((FloatValue)other).value;
if (value == otherFloat) return 0;
if (value < otherFloat) return -1;
return +1;
}
if (other instanceof DoubleValue) {
return super.compareTo(other);
}
try {
return compareTo(((NumericValue)other).convertPrimitive(BuiltInAtomicType.FLOAT, true).asAtomic());
} catch (XPathException err) {
throw new ClassCastException("Operand of comparison cannot be promoted to xs:float");
}
}
/**
* Compare the value to a long
* @param other the value to be compared with
* @return -1 if this is less, 0 if this is equal, +1 if this is greater or if this is NaN
*/
public int compareTo(long other) {
float otherFloat = (float)other;
if (value == otherFloat) return 0;
if (value < otherFloat) return -1;
return +1;
}
/**
* Get an object that implements XML Schema comparison semantics
*/
private Comparable getSchemaComparable() {
return new Float(value);
}
}
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.