org.python.core.PyFloat Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jython-slim Show documentation
Show all versions of jython-slim Show documentation
Jython is an implementation of the high-level, dynamic, object-oriented
language Python written in 100% Pure Java, and seamlessly integrated with
the Java platform. It thus allows you to run Python on any Java platform.
// 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.core.stringlib.FloatFormatter;
import org.python.core.stringlib.InternalFormat;
import org.python.core.stringlib.InternalFormat.Formatter;
import org.python.core.stringlib.InternalFormat.Spec;
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;
import org.python.modules.math;
/**
* A builtin python float.
*/
@Untraversable
@ExposedType(name = "float", doc = BuiltinDocs.float_doc)
public class PyFloat extends PyObject {
public static final PyType TYPE = PyType.fromClass(PyFloat.class);
/** Format specification used by repr(). */
static final Spec SPEC_REPR = InternalFormat.fromText(" >r");
/** Format specification used by str(). */
static final Spec SPEC_STR = Spec.NUMERIC;
/** Constant float(0). */
static final PyFloat ZERO = new PyFloat(0.0);
/** Constant float(1). */
static final PyFloat ONE = new PyFloat(1.0);
/** Constant float("nan"). */
static final PyFloat NAN = new PyFloat(Double.NaN);
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 ZERO;
} 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 ZERO;
}
@ExposedClassMethod(doc = BuiltinDocs.float_fromhex_doc)
public static PyObject float_fromhex(PyType type, PyObject o) {
// XXX: I'm sure this could be shortened/simplified, but Double.parseDouble() takes
// non-hex strings and requires the form 0xNUMBERpNUMBER for hex input which
// causes extra complexity here.
String message = "invalid hexadecimal floating-point string";
boolean negative = false;
PyString s = o.__str__();
String value = s.getString().trim().toLowerCase();
if (value.length() == 0) {
throw Py.ValueError(message);
} else if (value.equals("nan") || value.equals("-nan") || value.equals("+nan")) {
return NAN;
} else if (value.equals("inf") || value.equals("infinity") || value.equals("+inf")
|| value.equals("+infinity")) {
return new PyFloat(Double.POSITIVE_INFINITY);
} else if (value.equals("-inf") || value.equals("-infinity")) {
return new PyFloat(Double.NEGATIVE_INFINITY);
}
// Strip and record + or -
if (value.charAt(0) == '-') {
value = value.substring(1);
negative = true;
} else if (value.charAt(0) == '+') {
value = value.substring(1);
}
if (value.length() == 0) {
throw Py.ValueError(message);
}
// Append 0x if not present.
if (!value.startsWith("0x") && !value.startsWith("0X")) {
value = "0x" + value;
}
// reattach - if needed.
if (negative) {
value = "-" + value;
}
// Append p if not present.
if (value.indexOf('p') == -1) {
value = value + "p0";
}
try {
double d = Double.parseDouble(value);
if (Double.isInfinite(d)) {
throw Py.OverflowError("hexadecimal value too large to represent as a float");
}
return new PyFloat(d);
} catch (NumberFormatException n) {
throw Py.ValueError(message);
}
}
// @ExposedClassMethod(doc = BuiltinDocs.float_hex_doc)
// public static PyObject float_hex(PyType type, double value) {
// return new PyString(Double.toHexString(value));
// }
private String pyHexString(Double f) {
// Simply rewrite Java hex repr to expected Python values; not
// the most efficient, but we don't expect this to be a hot
// spot in our code either
String java_hex = Double.toHexString(getValue());
if (java_hex.equals("Infinity")) {
return "inf";
} else if (java_hex.equals("-Infinity")) {
return "-inf";
} else if (java_hex.equals("NaN")) {
return "nan";
} else if (java_hex.equals("0x0.0p0")) {
return "0x0.0p+0";
} else if (java_hex.equals("-0x0.0p0")) {
return "-0x0.0p+0";
}
// replace hex rep of MpE to conform with Python such that
// 1. M is padded to 16 digits (ignoring a leading -)
// 2. Mp+E if E>=0
// example: result of 42.0.hex() is translated from
// 0x1.5p5 to 0x1.5000000000000p+5
int len = java_hex.length();
boolean start_exponent = false;
StringBuilder py_hex = new StringBuilder(len + 1);
int padding = f > 0 ? 17 : 18;
for (int i = 0; i < len; i++) {
char c = java_hex.charAt(i);
if (c == 'p') {
for (int pad = i; pad < padding; pad++) {
py_hex.append('0');
}
start_exponent = true;
} else if (start_exponent) {
if (c != '-') {
py_hex.append('+');
}
start_exponent = false;
}
py_hex.append(c);
}
return py_hex.toString();
}
@ExposedMethod(doc = BuiltinDocs.float_hex_doc)
public PyObject float_hex() {
return new PyString(pyHexString(getValue()));
}
/**
* 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(SPEC_STR));
}
@Override
public PyString __repr__() {
return float___repr__();
}
@ExposedMethod(doc = BuiltinDocs.float___repr___doc)
final PyString float___repr__() {
return Py.newString(formatDouble(SPEC_REPR));
}
/**
* Format this float according to the specification passed in. Supports __str__
and
* __repr__
.
*
* @param spec parsed format specification string
* @return formatted value
*/
private String formatDouble(Spec spec) {
FloatFormatter f = new FloatFormatter(spec);
return f.format(value).getResult();
}
@Override
public int hashCode() {
return float___hash__();
}
@ExposedMethod(doc = BuiltinDocs.float___hash___doc)
final int float___hash__() {
double value = getValue();
if (Double.isInfinite(value)) {
return value < 0 ? -271828 : 314159;
} else if (Double.isNaN(value)) {
return 0;
}
double intPart = Math.floor(value);
double fractPart = value - intPart;
if (fractPart == 0) {
if (intPart <= Integer.MAX_VALUE && intPart >= Integer.MIN_VALUE) {
return (int)value;
} 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 Double.valueOf(getValue());
} else if (c == Float.TYPE || c == Float.class) {
return Float.valueOf((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 PyObject __gt__(PyObject other) {
// NaN > anything is always false.
if (Double.isNaN(getValue())) {
return Py.False;
}
return null;
}
@Override
public PyObject __ge__(PyObject other) {
// NaN >= anything is always false.
if (Double.isNaN(getValue())) {
return Py.False;
}
return null;
}
@Override
public PyObject __lt__(PyObject other) {
// NaN < anything is always false.
if (Double.isNaN(getValue())) {
return Py.False;
}
return null;
}
@Override
public PyObject __le__(PyObject other) {
// NaN >= anything is always false.
if (Double.isNaN(getValue())) {
return Py.False;
}
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());
} else 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;
} else if (Options.division_warning >= 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;
} else if (Options.division_warning >= 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());
}
/**
* Python % operator: y = n*x + z. The modulo operator always yields a result with the same sign
* as its second operand (or zero). (Compare java.Math.IEEEremainder
)
*
* @param x dividend
* @param y divisor
* @return x % y
*/
private static double modulo(double x, double y) {
if (y == 0.0) {
throw Py.ZeroDivisionError("float modulo");
} else {
double z = x % y;
if (z == 0.0) {
// Has to be same sign as y (even when zero).
return Math.copySign(z, y);
} else if ((z > 0.0) == (y > 0.0)) {
// z has same sign as y, as it must.
return z;
} else {
// Note abs(z) < abs(y) and opposite sign.
return z + y;
}
}
}
@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;
}
modulo = (modulo == Py.None) ? null : modulo;
if (modulo != null) {
throw Py.TypeError("pow() 3rd argument not allowed unless all arguments are integers");
} else {
return _pow(getValue(), coerce(right));
}
}
@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;
} else {
return _pow(coerce(left), getValue());
}
}
private static PyFloat _pow(double v, double w) {
/*
* This code was translated from the CPython implementation at v2.7.8 by progressively
* removing cases that could be delegated to Java. Jython differs from CPython in that where
* C pow() overflows, Java pow() returns inf (observed on Windows). This is not subject to
* regression tests, so we take it as an allowable platform dependency. All other
* differences in Java Math.pow() are trapped below and Python behaviour is enforced.
*/
if (w == 0) {
// v**0 is 1, even 0**0
return ONE;
} else if (Double.isNaN(v)) {
// nan**w = nan, unless w == 0
return NAN;
} else if (Double.isNaN(w)) {
// v**nan = nan, unless v == 1; 1**nan = 1
if (v == 1.0) {
return ONE;
} else {
return NAN;
}
} else if (Double.isInfinite(w)) {
/*
* In Java Math pow(1,inf) = pow(-1,inf) = pow(1,-inf) = pow(-1,-inf) = nan, but in
* Python they are all 1.
*/
if (v == 1.0 || v == -1.0) {
return ONE;
}
} else if (v == 0.0) {
// 0**w is an error if w is negative.
if (w < 0.0) {
throw Py.ZeroDivisionError("0.0 cannot be raised to a negative power");
}
} else if (!Double.isInfinite(v) && v < 0.0) {
if (w != Math.floor(w)) {
throw Py.ValueError("negative number cannot be raised to a fractional power");
}
}
// In all cases not caught above we can entrust the calculation to Java
return new PyFloat(Math.pow(v, w));
}
@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__() {
return new PyFloat(Math.abs(getValue()));
}
@Override
public PyObject __int__() {
return float___int__();
}
/** Smallest value that cannot be represented as an int */
private static double INT_LONG_BOUNDARY = -(double)Integer.MIN_VALUE; // 2^31
@ExposedMethod(doc = BuiltinDocs.float___int___doc)
final PyObject float___int__() {
double v = getValue();
if (v < INT_LONG_BOUNDARY && v > -(INT_LONG_BOUNDARY + 1.0)) {
// v will fit into an int (when rounded towards zero).
return new PyInteger((int)v);
} else {
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 PyObject __trunc__() {
return float___trunc__();
}
@ExposedMethod(doc = BuiltinDocs.float___trunc___doc)
final PyObject float___trunc__() {
if (Double.isNaN(value)) {
throw Py.ValueError("cannot convert float NaN to integer");
}
if (Double.isInfinite(value)) {
throw Py.OverflowError("cannot convert float infinity to integer");
}
if (value < Integer.MAX_VALUE) {
return new PyInteger((int)value);
} else if (value < Long.MAX_VALUE) {
return new PyLong((long)value);
}
BigDecimal d = new BigDecimal(value);
return new PyLong(d.toBigInteger());
}
@Override
public PyObject conjugate() {
return float_conjugate();
}
@ExposedMethod(doc = BuiltinDocs.float_conjugate_doc)
final PyObject float_conjugate() {
return this;
}
public boolean is_integer() {
return float_is_integer();
}
@ExposedMethod(doc = BuiltinDocs.float_is_integer_doc)
final boolean float_is_integer() {
if (Double.isInfinite(value)) {
return false;
}
return Math.floor(value) == value;
}
@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 PyObject __format__(PyObject formatSpec) {
return float___format__(formatSpec);
}
@ExposedMethod(doc = BuiltinDocs.float___format___doc)
final PyObject float___format__(PyObject formatSpec) {
// Parse the specification
Spec spec = InternalFormat.fromText(formatSpec, "__format__");
// Get a formatter for the specification
FloatFormatter f = prepareFormatter(spec);
if (f != null) {
// Bytes mode if formatSpec argument is not unicode.
f.setBytes(!(formatSpec instanceof PyUnicode));
// Convert as per specification.
f.format(value);
// Return a result that has the same type (str or unicode) as the formatSpec argument.
return f.pad().getPyResult();
} else {
// The type code was not recognised in prepareFormatter
throw Formatter.unknownFormat(spec.type, "float");
}
}
/**
* Common code for PyFloat, {@link PyInteger} and {@link PyLong} to prepare a
* {@link FloatFormatter} from a parsed specification. The object returned has format method
* {@link FloatFormatter#format(double)}.
*
* @param spec a parsed PEP-3101 format specification.
* @return a formatter ready to use, or null if the type is not a floating point format type.
* @throws PyException {@code ValueError} if the specification is faulty.
*/
@SuppressWarnings("fallthrough")
static FloatFormatter prepareFormatter(Spec spec) {
// Slight differences between format types
switch (spec.type) {
case 'n':
if (spec.grouping) {
throw Formatter.notAllowed("Grouping", "float", spec.type);
}
// Fall through
case Spec.NONE:
case 'e':
case 'f':
case 'g':
case 'E':
case 'F':
case 'G':
case '%':
// Check for disallowed parts of the specification
if (spec.alternate) {
throw FloatFormatter.alternateFormNotAllowed("float");
}
// spec may be incomplete. The defaults are those commonly used for numeric formats.
spec = spec.withDefaults(Spec.NUMERIC);
return new FloatFormatter(spec);
default:
return null;
}
}
@ExposedMethod(doc = BuiltinDocs.float_as_integer_ratio_doc)
public PyTuple as_integer_ratio() {
if (Double.isInfinite(value)) {
throw Py.OverflowError("Cannot pass infinity to float.as_integer_ratio.");
}
if (Double.isNaN(value)) {
throw Py.ValueError("Cannot pass NaN to float.as_integer_ratio.");
}
PyTuple frexp = math.frexp(value);
double float_part = ((Double)frexp.get(0)).doubleValue();
int exponent = ((Integer)frexp.get(1)).intValue();
for (int i = 0; i < 300 && float_part != Math.floor(float_part); i++) {
float_part *= 2.0;
exponent--;
}
/*
* self == float_part * 2**exponent exactly and float_part is integral. If FLT_RADIX != 2,
* the 300 steps may leave a tiny fractional part to be truncated by PyLong_FromDouble().
*/
PyLong numerator = new PyLong(float_part);
PyLong denominator = new PyLong(1);
PyLong py_exponent = new PyLong(Math.abs(exponent));
py_exponent = (PyLong)denominator.__lshift__(py_exponent);
if (exponent > 0) {
numerator = new PyLong(numerator.getValue().multiply(py_exponent.getValue()));
} else {
denominator = py_exponent;
}
return new PyTuple(numerator, denominator);
}
@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;
}
}
}
}