/*
* Copyright (c) Corporation for National Research Initiatives
* Copyright (c) Jython Developers
*/
package org.python.core;
import java.io.Serializable;
import java.math.BigDecimal;
import org.python.expose.ExposedClassMethod;
import org.python.expose.ExposedGet;
import org.python.expose.ExposedMethod;
import org.python.expose.ExposedNew;
import org.python.expose.ExposedType;
import org.python.expose.MethodType;
/**
* A builtin python float.
*/
@ExposedType(name = "float", doc = BuiltinDocs.float_doc)
public class PyFloat extends PyObject {
public static final PyType TYPE = PyType.fromClass(PyFloat.class);
/** Precisions used by repr() and str(), respectively. */
private static final int PREC_REPR = 17;
private static final int PREC_STR = 12;
private final double value;
public double getValue() {
return value;
}
public PyFloat(PyType subtype, double v) {
super(subtype);
value = v;
}
public PyFloat(double v) {
this(TYPE, v);
}
public PyFloat(float v) {
this((double) v);
}
@ExposedNew
public static PyObject float_new(PyNewWrapper new_, boolean init, PyType subtype,
PyObject[] args, String[] keywords) {
ArgParser ap = new ArgParser("float", args, keywords, new String[] {"x"}, 0);
PyObject x = ap.getPyObject(0, null);
if (x == null) {
if (new_.for_type == subtype) {
return new PyFloat(0.0);
} else {
return new PyFloatDerived(subtype, 0.0);
}
} else {
PyFloat floatObject = null;
try {
floatObject = x.__float__();
} catch (PyException e) {
if (e.match(Py.AttributeError)) {
// Translate AttributeError to TypeError
// XXX: We are using the same message as CPython, even if
// it is not strictly correct (instances of types
// that implement the __float__ method are also
// valid arguments)
throw Py.TypeError("float() argument must be a string or a number");
}
throw e;
}
if (new_.for_type == subtype) {
return floatObject;
} else {
return new PyFloatDerived(subtype, floatObject.getValue());
}
}
}
@ExposedGet(name = "real", doc = BuiltinDocs.float_real_doc)
public PyObject getReal() {
return float___float__();
}
@ExposedGet(name = "imag", doc = BuiltinDocs.float_imag_doc)
public PyObject getImag() {
return Py.newFloat(0.0);
}
/**
* Determine if this float is not infinity, nor NaN.
*/
public boolean isFinite() {
return !Double.isInfinite(getValue()) && !Double.isNaN(getValue());
}
@Override
public String toString() {
return __str__().toString();
}
@Override
public PyString __str__() {
return float___str__();
}
@ExposedMethod(doc = BuiltinDocs.float___str___doc)
final PyString float___str__() {
return Py.newString(formatDouble(PREC_STR));
}
@Override
public PyString __repr__() {
return float___repr__();
}
@ExposedMethod(doc = BuiltinDocs.float___repr___doc)
final PyString float___repr__() {
return Py.newString(formatDouble(PREC_REPR));
}
private String formatDouble(int precision) {
if (Double.isNaN(getValue())) {
return "nan";
}
String result = String.format("%%.%dg", precision);
result = Py.newString(result).__mod__(this).toString();
int i = 0;
if (result.startsWith("-")) {
i++;
}
for (; i < result.length(); i++) {
if (!Character.isDigit(result.charAt(i))) {
break;
}
}
if (i == result.length()) {
result += ".0";
}
return result;
}
@Override
public int hashCode() {
return float___hash__();
}
@ExposedMethod(doc = BuiltinDocs.float___hash___doc)
final int float___hash__() {
double intPart = Math.floor(getValue());
double fractPart = getValue() - intPart;
if (fractPart == 0) {
if (intPart <= Integer.MAX_VALUE && intPart >= Integer.MIN_VALUE) {
return (int) getValue();
} else {
return __long__().hashCode();
}
} else {
long v = Double.doubleToLongBits(getValue());
return (int) v ^ (int) (v >> 32);
}
}
@Override
public boolean __nonzero__() {
return float___nonzero__();
}
@ExposedMethod(doc = BuiltinDocs.float___nonzero___doc)
final boolean float___nonzero__() {
return getValue() != 0;
}
@Override
public Object __tojava__(Class<?> c) {
if (c == Double.TYPE || c == Number.class || c == Double.class || c == Object.class
|| c == Serializable.class) {
return new Double(getValue());
}
if (c == Float.TYPE || c == Float.class) {
return new Float(getValue());
}
return super.__tojava__(c);
}
@Override
public PyObject __eq__(PyObject other) {
// preclude _cmp_unsafe's this == other shortcut because NaN != anything, even
// itself
if (Double.isNaN(getValue())) {
return Py.False;
}
return null;
}
@Override
public PyObject __ne__(PyObject other) {
if (Double.isNaN(getValue())) {
return Py.True;
}
return null;
}
@Override
public int __cmp__(PyObject other) {
return float___cmp__(other);
}
// XXX: needs __doc__
@ExposedMethod(type = MethodType.CMP)
final int float___cmp__(PyObject other) {
double i = getValue();
double j;
if (other instanceof PyFloat) {
j = ((PyFloat) other).getValue();
} else if (!isFinite()) {
// we're infinity: our magnitude exceeds any finite
// integer, so it doesn't matter which int we compare i
// with. If NaN, similarly.
if (other instanceof PyInteger || other instanceof PyLong) {
j = 0.0;
} else {
return -2;
}
} else if (other instanceof PyInteger) {
j = ((PyInteger) other).getValue();
} else if (other instanceof PyLong) {
BigDecimal v = new BigDecimal(getValue());
BigDecimal w = new BigDecimal(((PyLong) other).getValue());
return v.compareTo(w);
} else {
return -2;
}
if (i < j) {
return -1;
} else if (i > j) {
return 1;
} else if (i == j) {
return 0;
} else {
// at least one side is NaN
return Double.isNaN(i) ? (Double.isNaN(j) ? 1 : -1) : 1;
}
}
@Override
public Object __coerce_ex__(PyObject other) {
return float___coerce_ex__(other);
}
@ExposedMethod(doc = BuiltinDocs.float___coerce___doc)
final PyObject float___coerce__(PyObject other) {
return adaptToCoerceTuple(float___coerce_ex__(other));
}
/**
* Coercion logic for float. Implemented as a final method to avoid
* invocation of virtual methods from the exposed coerce.
*/
final Object float___coerce_ex__(PyObject other) {
if (other instanceof PyFloat) {
return other;
} else {
if (other instanceof PyInteger) {
return new PyFloat((double) ((PyInteger) other).getValue());
}
if (other instanceof PyLong) {
return new PyFloat(((PyLong) other).doubleValue());
} else {
return Py.None;
}
}
}
private static boolean canCoerce(PyObject other) {
return other instanceof PyFloat || other instanceof PyInteger || other instanceof PyLong;
}
private static double coerce(PyObject other) {
if (other instanceof PyFloat) {
return ((PyFloat) other).getValue();
} else if (other instanceof PyInteger) {
return ((PyInteger) other).getValue();
} else if (other instanceof PyLong) {
return ((PyLong) other).doubleValue();
} else {
throw Py.TypeError("xxx");
}
}
@Override
public PyObject __add__(PyObject right) {
return float___add__(right);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___add___doc)
final PyObject float___add__(PyObject right) {
if (!canCoerce(right)) {
return null;
}
double rightv = coerce(right);
return new PyFloat(getValue() + rightv);
}
@Override
public PyObject __radd__(PyObject left) {
return float___radd__(left);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___radd___doc)
final PyObject float___radd__(PyObject left) {
return __add__(left);
}
@Override
public PyObject __sub__(PyObject right) {
return float___sub__(right);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___sub___doc)
final PyObject float___sub__(PyObject right) {
if (!canCoerce(right)) {
return null;
}
double rightv = coerce(right);
return new PyFloat(getValue() - rightv);
}
@Override
public PyObject __rsub__(PyObject left) {
return float___rsub__(left);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___rsub___doc)
final PyObject float___rsub__(PyObject left) {
if (!canCoerce(left)) {
return null;
}
double leftv = coerce(left);
return new PyFloat(leftv - getValue());
}
@Override
public PyObject __mul__(PyObject right) {
return float___mul__(right);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___mul___doc)
final PyObject float___mul__(PyObject right) {
if (!canCoerce(right)) {
return null;
}
double rightv = coerce(right);
return new PyFloat(getValue() * rightv);
}
@Override
public PyObject __rmul__(PyObject left) {
return float___rmul__(left);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___rmul___doc)
final PyObject float___rmul__(PyObject left) {
return __mul__(left);
}
@Override
public PyObject __div__(PyObject right) {
return float___div__(right);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___div___doc)
final PyObject float___div__(PyObject right) {
if (!canCoerce(right)) {
return null;
}
if (Options.divisionWarning >= 2) {
Py.warning(Py.DeprecationWarning, "classic float division");
}
double rightv = coerce(right);
if (rightv == 0) {
throw Py.ZeroDivisionError("float division");
}
return new PyFloat(getValue() / rightv);
}
@Override
public PyObject __rdiv__(PyObject left) {
return float___rdiv__(left);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___rdiv___doc)
final PyObject float___rdiv__(PyObject left) {
if (!canCoerce(left)) {
return null;
}
if (Options.divisionWarning >= 2) {
Py.warning(Py.DeprecationWarning, "classic float division");
}
double leftv = coerce(left);
if (getValue() == 0) {
throw Py.ZeroDivisionError("float division");
}
return new PyFloat(leftv / getValue());
}
@Override
public PyObject __floordiv__(PyObject right) {
return float___floordiv__(right);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___floordiv___doc)
final PyObject float___floordiv__(PyObject right) {
if (!canCoerce(right)) {
return null;
}
double rightv = coerce(right);
if (rightv == 0) {
throw Py.ZeroDivisionError("float division");
}
return new PyFloat(Math.floor(getValue() / rightv));
}
@Override
public PyObject __rfloordiv__(PyObject left) {
return float___rfloordiv__(left);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___rfloordiv___doc)
final PyObject float___rfloordiv__(PyObject left) {
if (!canCoerce(left)) {
return null;
}
double leftv = coerce(left);
if (getValue() == 0) {
throw Py.ZeroDivisionError("float division");
}
return new PyFloat(Math.floor(leftv / getValue()));
}
@Override
public PyObject __truediv__(PyObject right) {
return float___truediv__(right);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___truediv___doc)
final PyObject float___truediv__(PyObject right) {
if (!canCoerce(right)) {
return null;
}
double rightv = coerce(right);
if (rightv == 0) {
throw Py.ZeroDivisionError("float division");
}
return new PyFloat(getValue() / rightv);
}
@Override
public PyObject __rtruediv__(PyObject left) {
return float___rtruediv__(left);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___rtruediv___doc)
final PyObject float___rtruediv__(PyObject left) {
if (!canCoerce(left)) {
return null;
}
double leftv = coerce(left);
if (getValue() == 0) {
throw Py.ZeroDivisionError("float division");
}
return new PyFloat(leftv / getValue());
}
private static double modulo(double x, double y) {
if (y == 0) {
throw Py.ZeroDivisionError("float modulo");
}
double z = Math.IEEEremainder(x, y);
if (z * y < 0) {
z += y;
}
return z;
}
@Override
public PyObject __mod__(PyObject right) {
return float___mod__(right);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___mod___doc)
final PyObject float___mod__(PyObject right) {
if (!canCoerce(right)) {
return null;
}
double rightv = coerce(right);
return new PyFloat(modulo(getValue(),rightv));
}
@Override
public PyObject __rmod__(PyObject left) {
return float___rmod__(left);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___rmod___doc)
final PyObject float___rmod__(PyObject left) {
if (!canCoerce(left)) {
return null;
}
double leftv = coerce(left);
return new PyFloat(modulo(leftv, getValue()));
}
@Override
public PyObject __divmod__(PyObject right) {
return float___divmod__(right);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___divmod___doc)
final PyObject float___divmod__(PyObject right) {
if (!canCoerce(right)) {
return null;
}
double rightv = coerce(right);
if (rightv == 0) {
throw Py.ZeroDivisionError("float division");
}
double z = Math.floor(getValue() / rightv);
return new PyTuple(new PyFloat(z), new PyFloat(getValue() - z * rightv));
}
@Override
public PyObject __rdivmod__(PyObject left) {
if (!canCoerce(left)) {
return null;
}
double leftv = coerce(left);
if (getValue() == 0) {
throw Py.ZeroDivisionError("float division");
}
double z = Math.floor(leftv / getValue());
return new PyTuple(new PyFloat(z), new PyFloat(leftv - z * getValue()));
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___rdivmod___doc)
final PyObject float___rdivmod__(PyObject left) {
return __rdivmod__(left);
}
@Override
public PyObject __pow__(PyObject right, PyObject modulo) {
return float___pow__(right, modulo);
}
@ExposedMethod(type = MethodType.BINARY, defaults = "null",
doc = BuiltinDocs.float___pow___doc)
final PyObject float___pow__(PyObject right, PyObject modulo) {
if (!canCoerce(right)) {
return null;
}
if (modulo != null) {
throw Py.TypeError("pow() 3rd argument not allowed unless all arguments are integers");
}
return _pow( getValue(), coerce(right), modulo);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.float___rpow___doc)
final PyObject float___rpow__(PyObject left) {
return __rpow__(left);
}
@Override
public PyObject __rpow__(PyObject left) {
if (!canCoerce(left)) {
return null;
}
return _pow(coerce(left), getValue(), null);
}
private static PyFloat _pow(double value, double iw, PyObject modulo) {
// Rely completely on Java's pow function
if (iw == 0) {
if (modulo != null) {
return new PyFloat(modulo(1.0, coerce(modulo)));
}
return new PyFloat(1.0);
}
if (value == 0.0) {
if (iw < 0.0) {
throw Py.ZeroDivisionError("0.0 cannot be raised to a negative power");
}
return new PyFloat(0);
}
if (value < 0 && iw != Math.floor(iw)) {
throw Py.ValueError("negative number cannot be raised to a fractional power");
}
double ret = Math.pow(value, iw);
return new PyFloat(modulo == null ? ret : modulo(ret, coerce(modulo)));
}
@Override
public PyObject __neg__() {
return float___neg__();
}
@ExposedMethod(doc = BuiltinDocs.float___neg___doc)
final PyObject float___neg__() {
return new PyFloat(-getValue());
}
@Override
public PyObject __pos__() {
return float___pos__();
}
@ExposedMethod(doc = BuiltinDocs.float___pos___doc)
final PyObject float___pos__() {
return float___float__();
}
@Override
public PyObject __invert__() {
throw Py.TypeError("bad operand type for unary ~");
}
@Override
public PyObject __abs__() {
return float___abs__();
}
@ExposedMethod(doc = BuiltinDocs.float___abs___doc)
final PyObject float___abs__() {
if (getValue() < 0) {
return float___neg__();
}
return float___float__();
}
@Override
public PyObject __int__() {
return float___int__();
}
@ExposedMethod(doc = BuiltinDocs.float___int___doc)
final PyObject float___int__() {
if (getValue() <= Integer.MAX_VALUE && getValue() >= Integer.MIN_VALUE) {
return new PyInteger((int) getValue());
}
return __long__();
}
@Override
public PyObject __long__() {
return float___long__();
}
@ExposedMethod(doc = BuiltinDocs.float___long___doc)
final PyObject float___long__() {
return new PyLong(getValue());
}
@Override
public PyFloat __float__() {
return float___float__();
}
@ExposedMethod(doc = BuiltinDocs.float___float___doc)
final PyFloat float___float__() {
return getType() == TYPE ? this : Py.newFloat(getValue());
}
@Override
public PyComplex __complex__() {
return new PyComplex(getValue(), 0.);
}
@ExposedMethod(doc = BuiltinDocs.float___getnewargs___doc)
final PyTuple float___getnewargs__() {
return new PyTuple(new PyObject[] {new PyFloat(getValue())});
}
@Override
public PyTuple __getnewargs__() {
return float___getnewargs__();
}
@Override
public double asDouble() {
return getValue();
}
@Override
public boolean isNumberType() {
return true;
}
// standard singleton issues apply here to __getformat__/__setformat__,
// but this is what Python demands
public enum Format {
UNKNOWN("unknown"),
BE("IEEE, big-endian"),
LE("IEEE, little-endian");
private final String format;
Format(String format) {
this.format = format;
}
public String format() {
return format;
}
}
// subset of IEEE-754, the JVM is big-endian
public static volatile Format double_format = Format.BE;
public static volatile Format float_format = Format.BE;
@ExposedClassMethod(doc = BuiltinDocs.float___getformat___doc)
public static String float___getformat__(PyType type, String typestr) {
if ("double".equals(typestr)) {
return double_format.format();
} else if ("float".equals(typestr)) {
return float_format.format();
} else {
throw Py.ValueError("__getformat__() argument 1 must be 'double' or 'float'");
}
}
@ExposedClassMethod(doc = BuiltinDocs.float___setformat___doc)
public static void float___setformat__(PyType type, String typestr, String format) {
Format new_format = null;
if (!"double".equals(typestr) && !"float".equals(typestr)) {
throw Py.ValueError("__setformat__() argument 1 must be 'double' or 'float'");
}
if (Format.LE.format().equals(format)) {
throw Py.ValueError(String.format("can only set %s format to 'unknown' or the "
+ "detected platform value", typestr));
} else if (Format.BE.format().equals(format)) {
new_format = Format.BE;
} else if (Format.UNKNOWN.format().equals(format)) {
new_format = Format.UNKNOWN;
} else {
throw Py.ValueError("__setformat__() argument 2 must be 'unknown', " +
"'IEEE, little-endian' or 'IEEE, big-endian'");
}
if (new_format != null) {
if ("double".equals(typestr)) {
double_format = new_format;
} else {
float_format = new_format;
}
}
}
}