Skip to content

Commit ad1e89a

Browse files
committed
Merge 64438: hex/oct/bin can show floats exactly.
1 parent 3b2bfb9 commit ad1e89a

4 files changed

Lines changed: 63 additions & 1 deletion

File tree

Include/floatobject.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ PyAPI_FUNC(PyObject *) _PyFloat_FormatAdvanced(PyObject *obj,
111111
Py_UNICODE *format_spec,
112112
Py_ssize_t format_spec_len);
113113

114+
PyAPI_FUNC(PyObject *) _float_to_base(PyObject *v, int base);
115+
114116
#ifdef __cplusplus
115117
}
116118
#endif

Lib/test/test_builtin.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,15 @@ def test_hex(self):
553553
self.assertEqual(hex(-16), '-0x10')
554554
self.assertEqual(hex(-16), '-0x10')
555555
self.assertRaises(TypeError, hex, {})
556+
self.assertEqual(hex(3.125), '0x19 * 2.0 ** -3')
557+
self.assertEqual(hex(0.0), '0x0 * 2.0 ** 0')
558+
for sv in float('nan'), float('inf'), float('-inf'):
559+
self.assertEqual(hex(sv), repr(sv))
560+
for i in range(100):
561+
x = random.expovariate(.05)
562+
self.assertEqual(eval(hex(x)), x, (x, hex(x), eval(hex(x))))
563+
self.assertEqual(eval(hex(-x)), -x)
564+
self.assertEqual(hex(-x), ('-' + hex(x)))
556565

557566
def test_id(self):
558567
id(None)
@@ -796,6 +805,15 @@ def test_oct(self):
796805
self.assertEqual(oct(-100), '-0o144')
797806
self.assertEqual(oct(-100), '-0o144')
798807
self.assertRaises(TypeError, oct, ())
808+
self.assertEqual(oct(3.125), '0o31 * 2.0 ** -3')
809+
self.assertEqual(oct(0.0), '0o0 * 2.0 ** 0')
810+
for sv in float('nan'), float('inf'), float('-inf'):
811+
self.assertEqual(oct(sv), repr(sv))
812+
for i in range(100):
813+
x = random.expovariate(.05)
814+
self.assertEqual(eval(oct(x)), x)
815+
self.assertEqual(eval(oct(-x)), -x)
816+
self.assertEqual(oct(-x), ('-' + oct(x)))
799817

800818
def write_testfile(self):
801819
# NB the first 4 lines are also used to test input, below
@@ -1213,6 +1231,15 @@ def test_bin(self):
12131231
self.assertEqual(bin(2**65-1), '0b' + '1' * 65)
12141232
self.assertEqual(bin(-(2**65)), '-0b1' + '0' * 65)
12151233
self.assertEqual(bin(-(2**65-1)), '-0b' + '1' * 65)
1234+
self.assertEqual(bin(3.125), '0b11001 * 2.0 ** -3')
1235+
self.assertEqual(bin(0.0), '0b0 * 2.0 ** 0')
1236+
for sv in float('nan'), float('inf'), float('-inf'):
1237+
self.assertEqual(bin(sv), repr(sv))
1238+
for i in range(100):
1239+
x = random.expovariate(.05)
1240+
self.assertEqual(eval(bin(x)), x)
1241+
self.assertEqual(eval(bin(-x)), -x)
1242+
self.assertEqual(bin(-x), ('-' + bin(x)))
12161243

12171244
class TestSorted(unittest.TestCase):
12181245

Objects/abstract.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1451,8 +1451,11 @@ PyObject *
14511451
PyNumber_ToBase(PyObject *n, int base)
14521452
{
14531453
PyObject *res = NULL;
1454-
PyObject *index = PyNumber_Index(n);
1454+
PyObject *index;
14551455

1456+
if (PyFloat_Check(n))
1457+
return _float_to_base(n, base);
1458+
index = PyNumber_Index(n);
14561459
if (!index)
14571460
return NULL;
14581461
if (PyLong_Check(index))

Objects/floatobject.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,36 @@ PyDoc_STRVAR(float_as_integer_ratio_doc,
11131113
">>> (-.25).as_integer_ratio()\n"
11141114
"(-1, 4)");
11151115

1116+
PyObject *
1117+
_float_to_base(PyObject *v, int base)
1118+
{
1119+
PyObject *mant, *conv, *result;
1120+
double x, fr;
1121+
int i, exp;
1122+
1123+
if (!PyFloat_Check(v)) {
1124+
PyErr_BadInternalCall();
1125+
return NULL;
1126+
}
1127+
CONVERT_TO_DOUBLE(v, x);
1128+
if (!Py_IS_FINITE(x))
1129+
return PyObject_Repr(v);
1130+
fr = frexp(x, &exp);
1131+
for (i=0; i<300 && fr != floor(fr) ; i++) {
1132+
fr *= 2.0;
1133+
exp--;
1134+
}
1135+
mant = PyLong_FromDouble(floor(fr));
1136+
if (mant == NULL)
1137+
return NULL;
1138+
conv = PyNumber_ToBase(mant, base);
1139+
Py_DECREF(mant);
1140+
if (conv == NULL)
1141+
return NULL;
1142+
result = PyUnicode_FromFormat("%U * 2.0 ** %d", conv, exp);
1143+
Py_DECREF(conv);
1144+
return result;
1145+
}
11161146

11171147
static PyObject *
11181148
float_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds);

0 commit comments

Comments
 (0)