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