diff options
Diffstat (limited to 'numpy/core')
-rw-r--r-- | numpy/core/arrayprint.py | 67 | ||||
-rw-r--r-- | numpy/core/src/multiarray/dragon4.c | 252 | ||||
-rw-r--r-- | numpy/core/src/multiarray/dragon4.h | 35 | ||||
-rw-r--r-- | numpy/core/src/multiarray/multiarraymodule.c | 80 | ||||
-rw-r--r-- | numpy/core/src/multiarray/scalartypes.c.src | 19 | ||||
-rw-r--r-- | numpy/core/tests/test_getlimits.py | 2 |
6 files changed, 223 insertions, 232 deletions
diff --git a/numpy/core/arrayprint.py b/numpy/core/arrayprint.py index 8253713be..bceb5db27 100644 --- a/numpy/core/arrayprint.py +++ b/numpy/core/arrayprint.py @@ -718,6 +718,7 @@ class FloatingFormat(object): if self.floatmode == 'fixed': trim, unique = 'k', False strs = (dragon4_positional(x, precision=self.precision, + fractional=True, unique=unique, trim=trim, sign=self.sign == '+') for x in non_zero) @@ -757,15 +758,20 @@ class FloatingFormat(object): return ' '*(self.pad_left + self.pad_right + 1 - len(ret)) + ret if self.exp_format: - return dragon4_scientific(x, precision=self.precision, + return dragon4_scientific(x, + precision=self.precision, unique=self.unique, - trim=self.trim, sign=self.sign == '+', + trim=self.trim, + sign=self.sign == '+', pad_left=self.pad_left, exp_digits=self.exp_size) else: - return dragon4_positional(x, precision=self.precision, + return dragon4_positional(x, + precision=self.precision, unique=self.unique, - trim=self.trim, sign=self.sign == '+', + fractional=True, + trim=self.trim, + sign=self.sign == '+', pad_left=self.pad_left, pad_right=self.pad_right) @@ -773,7 +779,7 @@ class FloatingFormat(object): def format_float_scientific(x, precision=None, unique=True, trim='k', sign=False, pad_left=None, exp_digits=None): """ - Format a floating-point scalar as a string in fractional notation. + Format a floating-point scalar as a string in scientific notation. Provides control over rounding, trimming and padding. Uses and assumes IEEE unbiased rounding. Uses the "Dragon4" algorithm. @@ -783,18 +789,17 @@ def format_float_scientific(x, precision=None, unique=True, trim='k', x : python float or numpy floating scalar Value to format. precision : non-negative integer, optional - Maximum number of fractional digits to print. May be ommited - if `unique` is `True`, but is required if unique is `False`. + Maximum number of fractional digits to print. May be omitted if + `unique` is `True`, but is required if unique is `False`. unique : boolean, optional - If `False`, output exactly `precision` fractional digits and round the - remaining value. Digits are generated as if printing an - infinite-precision value and stopping after `precision` digits. If `True`, use a digit-generation strategy which gives the shortest representation which uniquely identifies the floating-point number from other values of the same type, by judicious rounding. If `precision` - was omitted, print out the full unique representation, otherwise digit - generation is cut off after `precision` digits and the remaining value - is rounded. + was omitted, print all necessary digits, otherwise digit generation is + cut off after `precision` digits and the remaining value is rounded. + If `False`, digits are generated as if printing an infinite-precision + value and stopping after `precision` digits, rounding the remaining + value. trim : one of 'k', '.', '0', '-', optional Controls post-processing trimming of trailing digits, as follows: k : keep trailing zeros, keep decimal point (no trimming) @@ -833,16 +838,17 @@ def format_float_scientific(x, precision=None, unique=True, trim='k', precision = -1 if precision is None else precision pad_left = -1 if pad_left is None else pad_left exp_digits = -1 if exp_digits is None else exp_digits - return dragon4_scientific(x, precision=precision, unique=unique, trim=trim, - sign=sign, pad_left=pad_left, + return dragon4_scientific(x, precision=precision, unique=unique, + trim=trim, sign=sign, pad_left=pad_left, exp_digits=exp_digits) -def format_float_positional(x, precision=None, unique=True, trim='k', - sign=False, pad_left=None, pad_right=None): +def format_float_positional(x, precision=None, unique=True, + fractional=True, trim='k', sign=False, + pad_left=None, pad_right=None): """ - Format a floating-point scalar as a string in scientific notation. + Format a floating-point scalar as a string in positional notation. - Provides control over rounding, trimming and padding. Uses and assumes + Provides control over rounding, trimming and padding. Uses and assumes IEEE unbiased rounding. Uses the "Dragon4" algorithm. Parameters @@ -850,18 +856,22 @@ def format_float_positional(x, precision=None, unique=True, trim='k', x : python float or numpy floating scalar Value to format. precision : non-negative integer, optional - Maximum number of fractional digits to print. May be ommited - if `unique` is `True`, but is required if unique is `False`. + Maximum number of digits to print. May be omitted if `unique` is + `True`, but is required if unique is `False`. unique : boolean, optional - If `False`, output exactly `precision` fractional digits and round the - remaining value. Digits are generated as if printing an - infinite-precision value and stopping after `precision` digits. If `True`, use a digit-generation strategy which gives the shortest representation which uniquely identifies the floating-point number from other values of the same type, by judicious rounding. If `precision` - was omitted, print out the full unique representation, otherwise digit - generation is cut off after `precision` digits and the remaining value - is rounded. + was omitted, print out all necessary digits, otherwise digit generation + is cut off after `precision` digits and the remaining value is rounded. + If `False`, digits are generated as if printing an infinite-precision + value and stopping after `precision` digits, rounding the remaining + value. + fractional : boolean, optional + If `True`, the cutoff of `precision` digits refers to the total number + of digits after the decimal point, including leading zeros. + If `False`, `precision` refers to the total number of significant + digits, before or after the decimal point, ignoring leading zeros. trim : one of 'k', '.', '0', '-', optional Controls post-processing trimming of trailing digits, as follows: k : keep trailing zeros, keep decimal point (no trimming) @@ -901,7 +911,8 @@ def format_float_positional(x, precision=None, unique=True, trim='k', precision = -1 if precision is None else precision pad_left = -1 if pad_left is None else pad_left pad_right = -1 if pad_right is None else pad_right - return dragon4_positional(x, precision=precision, unique=unique, trim=trim, + return dragon4_positional(x, precision=precision, unique=unique, + fractional=fractional, trim=trim, sign=sign, pad_left=pad_left, pad_right=pad_right) diff --git a/numpy/core/src/multiarray/dragon4.c b/numpy/core/src/multiarray/dragon4.c index ab0741932..4bd055817 100644 --- a/numpy/core/src/multiarray/dragon4.c +++ b/numpy/core/src/multiarray/dragon4.c @@ -35,8 +35,12 @@ #include <string.h> #include <assert.h> -/* #define DEBUG_ASSERT(stmnt) assert(stmnt) */ + +#if 0 +#define DEBUG_ASSERT(stmnt) assert(stmnt) +#else #define DEBUG_ASSERT(stmnt) {} +#endif /* * Get the log base 2 of a 32-bit unsigned integer. @@ -951,28 +955,6 @@ BigInt_ShiftLeft(BigInt *result, npy_uint32 shift) } } -typedef enum CutoffMode -{ - /* - * As many digits as necessary to print a uniquely identifiable number. - * cutoffNumber should be -1. - */ - CutoffMode_Unique, - /* up to cutoffNumber significant digits */ - CutoffMode_TotalLength, - /* up to cutoffNumber significant digits past the decimal point */ - CutoffMode_FractionLength, - /* - * up to cutoffNumber digits, or fewer if the number can be uniquely - * identified with fewer - */ - CutoffMode_MaxTotalUnique, - /* - * up to cutoffNumber digits pas decimal point, or fewer if the number can - * be uniquely identified with fewer - */ - CutoffMode_MaxFractionUnique, -} CutoffMode; /* * This is an implementation the Dragon4 algorithm to convert a binary number in @@ -1029,7 +1011,8 @@ typedef enum CutoffMode static npy_uint32 Dragon4(const npy_uint64 mantissa, const npy_int32 exponent, const npy_uint32 mantissaBit, const npy_bool hasUnequalMargins, - const CutoffMode cutoffMode, npy_int32 cutoffNumber, char *pOutBuffer, + const DigitMode digitMode, const CutoffMode cutoffMode, + npy_int32 cutoffNumber, char *pOutBuffer, npy_uint32 bufferSize, npy_int32 *pOutExponent) { char *curDigit = pOutBuffer; @@ -1053,7 +1036,7 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent, BigInt optionalMarginHigh; const npy_float64 log10_2 = 0.30102999566398119521373889472449; - npy_int32 digitExponent, cutoffExponent, desiredCutoffExponent, hiBlock; + npy_int32 digitExponent, cutoffExponent, hiBlock; npy_uint32 outputDigit; /* current digit being output */ npy_uint32 outputLen; npy_bool isEven = (mantissa % 2) == 0; @@ -1187,8 +1170,7 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent, * increases the number. This will either correct digitExponent to an * accurate value or it will clamp it above the accurate value. */ - if ( (cutoffMode == CutoffMode_FractionLength || - cutoffMode == CutoffMode_MaxFractionUnique) && + if (cutoffNumber >= 0 && cutoffMode == CutoffMode_FractionLength && digitExponent <= -cutoffNumber) { digitExponent = -cutoffNumber + 1; } @@ -1247,29 +1229,24 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent, * Default to the maximum size of the output buffer. */ cutoffExponent = digitExponent - bufferSize; - switch(cutoffMode) { - /* print digits until we pass the accuracy margin or buffer size */ - case CutoffMode_Unique: - DEBUG_ASSERT(cutoffNumber == -1); - break; - /* print cutoffNumber of digits or until we reach the buffer size */ - case CutoffMode_MaxTotalUnique: - case CutoffMode_TotalLength: + if (cutoffNumber >= 0) { + npy_int32 desiredCutoffExponent; + + if (cutoffMode == CutoffMode_TotalLength) { desiredCutoffExponent = digitExponent - cutoffNumber; if (desiredCutoffExponent > cutoffExponent) { cutoffExponent = desiredCutoffExponent; } - break; - /* print cutoffNumber digits past the decimal point or until we reach - * the buffer size + } + /* Otherwise it's CutoffMode_FractionLength. Print cutoffNumber digits + * past the decimal point or until we reach the buffer size */ - case CutoffMode_MaxFractionUnique: - case CutoffMode_FractionLength: + else { desiredCutoffExponent = -cutoffNumber; if (desiredCutoffExponent > cutoffExponent) { cutoffExponent = desiredCutoffExponent; } - break; + } } /* Output the exponent of the first digit we will print */ @@ -1311,9 +1288,7 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent, } } - if (cutoffMode == CutoffMode_Unique || - cutoffMode == CutoffMode_MaxFractionUnique || - cutoffMode == CutoffMode_MaxTotalUnique) { + if (digitMode == DigitMode_Unique) { /* * For the unique cutoff mode, we will try to print until we have * reached a level of precision that uniquely distinguishes this value @@ -1358,7 +1333,7 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent, } else { /* - * For length based cutoff modes, we will try to print until we + * For exact digit mode, we will try to print until we * have exhausted all precision (i.e. all remaining digits are zeros) or * until we reach the desired cutoff digit. */ @@ -1607,31 +1582,22 @@ typedef union FloatUnion80 static npy_uint32 FormatPositional(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa, npy_int32 exponent, char signbit, npy_uint32 mantissaBit, - npy_bool hasUnequalMargins, npy_bool unique, - npy_int32 precision, TrimMode trim_mode, - npy_int32 digits_left, npy_int32 digits_right) + npy_bool hasUnequalMargins, DigitMode digit_mode, + CutoffMode cutoff_mode, npy_int32 precision, + TrimMode trim_mode, npy_int32 digits_left, + npy_int32 digits_right) { npy_int32 printExponent; npy_int32 numDigits, numWholeDigits, has_sign=0; npy_int32 maxPrintLen = bufferSize - 1, pos = 0; - CutoffMode cutoffMode; /* track the # of digits past the decimal point that have been printed */ npy_int32 numFractionDigits = 0; DEBUG_ASSERT(bufferSize > 0); - if (unique) { - if (precision < 0) { - cutoffMode = CutoffMode_Unique; - } - else { - cutoffMode = CutoffMode_MaxFractionUnique; - } - } - else { - cutoffMode = CutoffMode_FractionLength; + if (digit_mode != DigitMode_Unique) { DEBUG_ASSERT(precision >= 0); } @@ -1645,7 +1611,7 @@ FormatPositional(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa, } numDigits = Dragon4(mantissa, exponent, mantissaBit, hasUnequalMargins, - cutoffMode, precision, buffer + has_sign, + digit_mode, cutoff_mode, precision, buffer + has_sign, maxPrintLen - has_sign, &printExponent); DEBUG_ASSERT(numDigits > 0); @@ -1752,7 +1718,7 @@ FormatPositional(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa, } } else if (trim_mode == TrimMode_None && - cutoffMode != CutoffMode_MaxFractionUnique && + digit_mode != DigitMode_Unique && precision > (npy_int32)numFractionDigits && pos < maxPrintLen) { /* add trailing zeros up to precision length */ /* compute the number of trailing zeros needed */ @@ -1848,7 +1814,7 @@ FormatPositional(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa, static npy_uint32 FormatScientific (char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa, npy_int32 exponent, char signbit, npy_uint32 mantissaBit, - npy_bool hasUnequalMargins, npy_bool unique, + npy_bool hasUnequalMargins, DigitMode digit_mode, npy_int32 precision, TrimMode trim_mode, npy_int32 digits_left, npy_int32 exp_digits) { @@ -1857,21 +1823,12 @@ FormatScientific (char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa, char *pCurOut; npy_int32 numFractionDigits; npy_int32 leftchars; - CutoffMode cutoffMode; - if (unique) { - if (precision < 0) { - cutoffMode = CutoffMode_Unique; - } - else { - cutoffMode = CutoffMode_MaxTotalUnique; - } - } - else { - cutoffMode = CutoffMode_TotalLength; + if (digit_mode != DigitMode_Unique) { DEBUG_ASSERT(precision >= 0); } + DEBUG_ASSERT(bufferSize > 0); pCurOut = buffer; @@ -1899,8 +1856,8 @@ FormatScientific (char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa, } numDigits = Dragon4(mantissa, exponent, mantissaBit, hasUnequalMargins, - cutoffMode, precision + 1, pCurOut, bufferSize, - &printExponent); + digit_mode, CutoffMode_TotalLength, precision + 1, + pCurOut, bufferSize, &printExponent); DEBUG_ASSERT(numDigits > 0); DEBUG_ASSERT(numDigits <= bufferSize); @@ -1943,7 +1900,7 @@ FormatScientific (char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa, } } else if (trim_mode == TrimMode_None && - cutoffMode != CutoffMode_MaxTotalUnique) { + digit_mode != DigitMode_Unique) { /* add trailing zeros up to precision length */ if (precision > (npy_int32)numFractionDigits) { char *pEnd; @@ -2148,7 +2105,8 @@ PrintInfNan(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa, */ static npy_uint32 Dragon4_PrintFloat16(char *buffer, npy_uint32 bufferSize, npy_uint16 value, - npy_bool scientific, npy_bool unique, npy_int32 precision, + npy_bool scientific, DigitMode digit_mode, + CutoffMode cutoff_mode, npy_int32 precision, npy_bool sign, TrimMode trim_mode, npy_int32 digits_left, npy_int32 digits_right, npy_int32 exp_digits) { @@ -2231,20 +2189,21 @@ Dragon4_PrintFloat16(char *buffer, npy_uint32 bufferSize, npy_uint16 value, /* format the value */ if (scientific) { return FormatScientific(buffer, bufferSize, mantissa, exponent, signbit, - mantissaBit, hasUnequalMargins, unique, + mantissaBit, hasUnequalMargins, digit_mode, precision, trim_mode, digits_left, exp_digits); } else { return FormatPositional(buffer, bufferSize, mantissa, exponent, signbit, - mantissaBit, hasUnequalMargins, unique, - precision, trim_mode, + mantissaBit, hasUnequalMargins, digit_mode, + cutoff_mode, precision, trim_mode, digits_left, digits_right); } } static npy_uint32 Dragon4_PrintFloat32(char *buffer, npy_uint32 bufferSize, npy_float32 value, - npy_bool scientific, npy_bool unique, npy_int32 precision, + npy_bool scientific, DigitMode digit_mode, + CutoffMode cutoff_mode, npy_int32 precision, npy_bool sign, TrimMode trim_mode, npy_int32 digits_left, npy_int32 digits_right, npy_int32 exp_digits) { @@ -2327,20 +2286,21 @@ Dragon4_PrintFloat32(char *buffer, npy_uint32 bufferSize, npy_float32 value, /* format the value */ if (scientific) { return FormatScientific(buffer, bufferSize, mantissa, exponent, signbit, - mantissaBit, hasUnequalMargins, unique, + mantissaBit, hasUnequalMargins, digit_mode, precision, trim_mode, digits_left, exp_digits); } else { return FormatPositional(buffer, bufferSize, mantissa, exponent, signbit, - mantissaBit, hasUnequalMargins, unique, - precision, trim_mode, + mantissaBit, hasUnequalMargins, digit_mode, + cutoff_mode, precision, trim_mode, digits_left, digits_right); } } static npy_uint32 Dragon4_PrintFloat64(char *buffer, npy_uint32 bufferSize, npy_float64 value, - npy_bool scientific, npy_bool unique, npy_int32 precision, + npy_bool scientific, DigitMode digit_mode, + CutoffMode cutoff_mode, npy_int32 precision, npy_bool sign, TrimMode trim_mode, npy_int32 digits_left, npy_int32 digits_right, npy_int32 exp_digits) { @@ -2424,20 +2384,21 @@ Dragon4_PrintFloat64(char *buffer, npy_uint32 bufferSize, npy_float64 value, /* format the value */ if (scientific) { return FormatScientific(buffer, bufferSize, mantissa, exponent, signbit, - mantissaBit, hasUnequalMargins, unique, + mantissaBit, hasUnequalMargins, digit_mode, precision, trim_mode, digits_left, exp_digits); } else { return FormatPositional(buffer, bufferSize, mantissa, exponent, signbit, - mantissaBit, hasUnequalMargins, unique, - precision, trim_mode, + mantissaBit, hasUnequalMargins, digit_mode, + cutoff_mode, precision, trim_mode, digits_left, digits_right); } } static npy_uint32 Dragon4_PrintFloat128(char *buffer, npy_uint32 bufferSize, FloatVal128 value, - npy_bool scientific, npy_bool unique, npy_int32 precision, + npy_bool scientific, DigitMode digit_mode, + CutoffMode cutoff_mode, npy_int32 precision, npy_bool sign, TrimMode trim_mode, npy_int32 digits_left, npy_int32 digits_right, npy_int32 exp_digits) { @@ -2519,23 +2480,23 @@ Dragon4_PrintFloat128(char *buffer, npy_uint32 bufferSize, FloatVal128 value, /* format the value */ if (scientific) { return FormatScientific(buffer, bufferSize, mantissa, exponent, signbit, - mantissaBit, hasUnequalMargins, unique, + mantissaBit, hasUnequalMargins, digit_mode, precision, trim_mode, digits_left, exp_digits); } else { return FormatPositional(buffer, bufferSize, mantissa, exponent, signbit, - mantissaBit, hasUnequalMargins, unique, - precision, trim_mode, + mantissaBit, hasUnequalMargins, digit_mode, + cutoff_mode, precision, trim_mode, digits_left, digits_right); } } PyObject * -Dragon4_Positional_AnySize(void *val, size_t size, npy_bool unique, - int precision, int sign, TrimMode trim, - int pad_left, int pad_right) +Dragon4_Positional_AnySize(void *val, size_t size, DigitMode digit_mode, + CutoffMode cutoff_mode, int precision, int sign, + TrimMode trim, int pad_left, int pad_right) { - /* + /* * Use a very large buffer in case anyone tries to output a large numberG. * 16384 should be enough to uniquely print any float128, which goes up * to about 10^4932 */ @@ -2554,15 +2515,18 @@ Dragon4_Positional_AnySize(void *val, size_t size, npy_bool unique, switch (size) { case 2: Dragon4_PrintFloat16(repr, sizeof(repr), *(npy_float16*)val, - 0, unique, precision, sign, trim, pad_left, pad_right, -1); + 0, digit_mode, cutoff_mode, precision, + sign, trim, pad_left, pad_right, -1); break; case 4: Dragon4_PrintFloat32(repr, sizeof(repr), *(npy_float32*)val, - 0, unique, precision, sign, trim, pad_left, pad_right, -1); + 0, digit_mode, cutoff_mode, precision, + sign, trim, pad_left, pad_right, -1); break; case 8: Dragon4_PrintFloat64(repr, sizeof(repr), *(npy_float64*)val, - 0, unique, precision, sign, trim, pad_left, pad_right, -1); + 0, digit_mode, cutoff_mode, precision, + sign, trim, pad_left, pad_right, -1); break; #ifdef NPY_FLOAT80 case 10: @@ -2570,7 +2534,8 @@ Dragon4_Positional_AnySize(void *val, size_t size, npy_bool unique, val128.integer[0] = buf80.integer.a; val128.integer[1] = buf80.integer.b; Dragon4_PrintFloat128(repr, sizeof(repr), val128, - 0, unique, precision, sign, trim, pad_left, pad_right, -1); + 0, digit_mode, cutoff_mode, precision, + sign, trim, pad_left, pad_right, -1); break; #endif #ifdef NPY_FLOAT96 @@ -2579,7 +2544,8 @@ Dragon4_Positional_AnySize(void *val, size_t size, npy_bool unique, val128.integer[0] = buf96.integer.a; val128.integer[1] = buf96.integer.b; Dragon4_PrintFloat128(repr, sizeof(repr), val128, - 0, unique, precision, sign, trim, pad_left, pad_right, -1); + 0, digit_mode, cutoff_mode, precision, + sign, trim, pad_left, pad_right, -1); break; #endif #ifdef NPY_FLOAT128 @@ -2588,7 +2554,8 @@ Dragon4_Positional_AnySize(void *val, size_t size, npy_bool unique, val128.integer[0] = buf128.integer.a; val128.integer[1] = buf128.integer.b; Dragon4_PrintFloat128(repr, sizeof(repr), val128, - 0, unique, precision, sign, trim, pad_left, pad_right, -1); + 0, digit_mode, cutoff_mode, precision, + sign, trim, pad_left, pad_right, -1); break; #endif default: @@ -2600,42 +2567,48 @@ Dragon4_Positional_AnySize(void *val, size_t size, npy_bool unique, } PyObject * -Dragon4_Positional(PyObject *obj, npy_bool unique, int precision, int sign, - TrimMode trim, int pad_left, int pad_right) +Dragon4_Positional(PyObject *obj, DigitMode digit_mode, CutoffMode cutoff_mode, + int precision, int sign, TrimMode trim, int pad_left, + int pad_right) { double val; if (PyArray_IsScalar(obj, Half)) { npy_half x = ((PyHalfScalarObject *)obj)->obval; - return Dragon4_Positional_AnySize(&x, sizeof(npy_half), unique, - precision, sign, trim, pad_left, pad_right); + return Dragon4_Positional_AnySize(&x, sizeof(npy_half), + digit_mode, cutoff_mode, precision, + sign, trim, pad_left, pad_right); } else if (PyArray_IsScalar(obj, Float)) { npy_float x = ((PyFloatScalarObject *)obj)->obval; - return Dragon4_Positional_AnySize(&x, sizeof(npy_float), unique, - precision, sign, trim, pad_left, pad_right); + return Dragon4_Positional_AnySize(&x, sizeof(npy_float), + digit_mode, cutoff_mode, precision, + sign, trim, pad_left, pad_right); } else if (PyArray_IsScalar(obj, Double)) { npy_double x = ((PyDoubleScalarObject *)obj)->obval; - return Dragon4_Positional_AnySize(&x, sizeof(npy_double), unique, - precision, sign, trim, pad_left, pad_right); + return Dragon4_Positional_AnySize(&x, sizeof(npy_double), + digit_mode, cutoff_mode, precision, + sign, trim, pad_left, pad_right); } else if (PyArray_IsScalar(obj, LongDouble)) { npy_longdouble x = ((PyLongDoubleScalarObject *)obj)->obval; - return Dragon4_Positional_AnySize(&x, sizeof(npy_longdouble), unique, - precision, sign, trim, pad_left, pad_right); + return Dragon4_Positional_AnySize(&x, sizeof(npy_longdouble), + digit_mode, cutoff_mode, precision, + sign, trim, pad_left, pad_right); } val = PyFloat_AsDouble(obj); if (PyErr_Occurred()) { return NULL; } - return Dragon4_Positional_AnySize(&val, sizeof(double), unique, - precision, sign, trim, pad_left, pad_right); + return Dragon4_Positional_AnySize(&val, sizeof(double), + digit_mode, cutoff_mode, precision, + sign, trim, pad_left, pad_right); } PyObject * -Dragon4_Scientific_AnySize(void *val, size_t size, npy_bool unique, +Dragon4_Scientific_AnySize(void *val, size_t size, DigitMode digit_mode, int precision, int sign, TrimMode trim, int pad_left, int exp_digits) { @@ -2652,19 +2625,24 @@ Dragon4_Scientific_AnySize(void *val, size_t size, npy_bool unique, FloatUnion128 buf128; #endif + /* dummy, is ignored in scientific mode */ + CutoffMode cutoff_mode = CutoffMode_TotalLength; switch (size) { case 2: Dragon4_PrintFloat16(repr, sizeof(repr), *(npy_float16*)val, - 1, unique, precision, sign, trim, pad_left, -1, exp_digits); + 1, digit_mode, cutoff_mode, precision, sign, + trim, pad_left, -1, exp_digits); break; case 4: Dragon4_PrintFloat32(repr, sizeof(repr), *(npy_float32*)val, - 1, unique, precision, sign, trim, pad_left, -1, exp_digits); + 1, digit_mode, cutoff_mode, precision, sign, + trim, pad_left, -1, exp_digits); break; case 8: Dragon4_PrintFloat64(repr, sizeof(repr), *(npy_float64*)val, - 1, unique, precision, sign, trim, pad_left, -1, exp_digits); + 1, digit_mode, cutoff_mode, precision, sign, + trim, pad_left, -1, exp_digits); break; #ifdef NPY_FLOAT80 case 10: @@ -2672,7 +2650,8 @@ Dragon4_Scientific_AnySize(void *val, size_t size, npy_bool unique, val128.integer[0] = buf80.integer.a; val128.integer[1] = buf80.integer.b; Dragon4_PrintFloat128(repr, sizeof(repr), val128, - 1, unique, precision, sign, trim, pad_left, -1, exp_digits); + 1, digit_mode, cutoff_mode, precision, sign, + trim, pad_left, -1, exp_digits); break; #endif #ifdef NPY_FLOAT96 @@ -2681,7 +2660,8 @@ Dragon4_Scientific_AnySize(void *val, size_t size, npy_bool unique, val128.integer[0] = buf96.integer.a; val128.integer[1] = buf96.integer.b; Dragon4_PrintFloat128(repr, sizeof(repr), val128, - 1, unique, precision, sign, trim, pad_left, -1, exp_digits); + 1, digit_mode, cutoff_mode, precision, sign, + trim, pad_left, -1, exp_digits); break; #endif #ifdef NPY_FLOAT128 @@ -2690,7 +2670,8 @@ Dragon4_Scientific_AnySize(void *val, size_t size, npy_bool unique, val128.integer[0] = buf128.integer.a; val128.integer[1] = buf128.integer.b; Dragon4_PrintFloat128(repr, sizeof(repr), val128, - 1, unique, precision, sign, trim, pad_left, -1, exp_digits); + 1, digit_mode, cutoff_mode, precision, sign, + trim, pad_left, -1, exp_digits); break; #endif default: @@ -2702,36 +2683,41 @@ Dragon4_Scientific_AnySize(void *val, size_t size, npy_bool unique, } PyObject * -Dragon4_Scientific(PyObject *obj, npy_bool unique, int precision, int sign, - TrimMode trim, int pad_left, int exp_digits) +Dragon4_Scientific(PyObject *obj, DigitMode digit_mode, int precision, + int sign, TrimMode trim, int pad_left, int exp_digits) { double val; if (PyArray_IsScalar(obj, Half)) { npy_half x = ((PyHalfScalarObject *)obj)->obval; - return Dragon4_Scientific_AnySize(&x, sizeof(npy_half), unique, - precision, sign, trim, pad_left, exp_digits); + return Dragon4_Scientific_AnySize(&x, sizeof(npy_half), + digit_mode, precision, + sign, trim, pad_left, exp_digits); } else if (PyArray_IsScalar(obj, Float)) { npy_float x = ((PyFloatScalarObject *)obj)->obval; - return Dragon4_Scientific_AnySize(&x, sizeof(npy_float), unique, - precision, sign, trim, pad_left, exp_digits); + return Dragon4_Scientific_AnySize(&x, sizeof(npy_float), + digit_mode, precision, + sign, trim, pad_left, exp_digits); } else if (PyArray_IsScalar(obj, Double)) { npy_double x = ((PyDoubleScalarObject *)obj)->obval; - return Dragon4_Scientific_AnySize(&x, sizeof(npy_double), unique, - precision, sign, trim, pad_left, exp_digits); + return Dragon4_Scientific_AnySize(&x, sizeof(npy_double), + digit_mode, precision, + sign, trim, pad_left, exp_digits); } else if (PyArray_IsScalar(obj, LongDouble)) { npy_longdouble x = ((PyLongDoubleScalarObject *)obj)->obval; - return Dragon4_Scientific_AnySize(&x, sizeof(npy_longdouble), unique, - precision, sign, trim, pad_left, exp_digits); + return Dragon4_Scientific_AnySize(&x, sizeof(npy_longdouble), + digit_mode, precision, + sign, trim, pad_left, exp_digits); } val = PyFloat_AsDouble(obj); if (PyErr_Occurred()) { return NULL; } - return Dragon4_Scientific_AnySize(&val, sizeof(double), unique, - precision, sign, trim, pad_left, exp_digits); + return Dragon4_Scientific_AnySize(&val, sizeof(double), + digit_mode, precision, + sign, trim, pad_left, exp_digits); } diff --git a/numpy/core/src/multiarray/dragon4.h b/numpy/core/src/multiarray/dragon4.h index 814c84a2f..5559c5157 100644 --- a/numpy/core/src/multiarray/dragon4.h +++ b/numpy/core/src/multiarray/dragon4.h @@ -40,6 +40,22 @@ #include "npy_pycompat.h" #include "numpy/arrayscalars.h" +typedef enum DigitMode +{ + /* Round digits to print shortest uniquely identifiable number. */ + DigitMode_Unique, + /* Output the digits of the number as if with infinite precision */ + DigitMode_Exact, +} DigitMode; + +typedef enum CutoffMode +{ + /* up to cutoffNumber significant digits */ + CutoffMode_TotalLength, + /* up to cutoffNumber significant digits past the decimal point */ + CutoffMode_FractionLength, +} CutoffMode; + typedef enum TrimMode { TrimMode_None, /* don't trim zeros, always leave a decimal point */ @@ -49,22 +65,23 @@ typedef enum TrimMode } TrimMode; PyObject * -Dragon4_Positional_AnySize(void *val, size_t size, npy_bool unique, - int precision, int sign, TrimMode trim, - int pad_left, int pad_right); +Dragon4_Positional_AnySize(void *val, size_t size, DigitMode digit_mode, + CutoffMode cutoff_mode, int precision, int sign, + TrimMode trim, int pad_left, int pad_right); PyObject * -Dragon4_Scientific_AnySize(void *val, size_t size, npy_bool unique, +Dragon4_Scientific_AnySize(void *val, size_t size, DigitMode digit_mode, int precision, int sign, TrimMode trim, - int pad_left, int exp_digits); + int pad_left, int pad_right); PyObject * -Dragon4_Positional(PyObject *obj, npy_bool unique, int precision, int sign, - TrimMode trim, int pad_left, int pad_right); +Dragon4_Positional(PyObject *obj, DigitMode digit_mode, CutoffMode cutoff_mode, + int precision, int sign, TrimMode trim, int pad_left, + int pad_right); PyObject * -Dragon4_Scientific(PyObject *obj, npy_bool unique, int precision, int sign, - TrimMode trim, int pad_left, int exp_digits); +Dragon4_Scientific(PyObject *obj, DigitMode digit_mode, int precision, + int sign, TrimMode trim, int pad_left, int exp_digits); #endif diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c index 9ee53362e..81971ad0f 100644 --- a/numpy/core/src/multiarray/multiarraymodule.c +++ b/numpy/core/src/multiarray/multiarraymodule.c @@ -3587,27 +3587,9 @@ as_buffer(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) /* * Prints floating-point scalars usign the Dragon4 algorithm, scientific mode. - * Arguments: - * x - a numpy scalar of Floating type - * precision - number of fractional digits to show. In unique mode, can be - * ommited and the unique repr will be returned, otherwise the - * unique value will be truncated to this number of digits - * (breaking the uniqueness guarantee). In fixed mode, is - * required, and specifies the number of fractional digits to - * print. - * unique - whether to use unique (default) or fixed mode. - * sign - whether to show the sign for positive values. Default False - * trim - one of 'k', '.', '0', '-' to control trailing digits, as follows: - * k : don't trim zeros, always leave a decimal point - * . : trim all but the zero before the decimal point - * 0 : trim all trailing zeros, leave decimal point - * - : trim trailing zeros and a trailing decimal point - * Default is k. - * pad_left - pads left side of string with whitespace until at least - * this many characters are to the left of the decimal point. If - * -1, don't add any padding. Default -1. - * exp_digits - exponent will contain at least this many digits, padding - * with 0 if necessary. -1 means pad to 2. Maximum of 5. + * See docstring of `np.format_float_scientific` for description of arguments. + * The differences is that a value of -1 is valid for pad_left, exp_digits, + * precision, which is equivalent to `None`. */ static PyObject * dragon4_scientific(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) @@ -3617,6 +3599,7 @@ dragon4_scientific(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) "pad_left", "exp_digits", NULL}; int precision=-1, pad_left=-1, exp_digits=-1; char *trimstr=NULL; + DigitMode digit_mode; TrimMode trim = TrimMode_None; int sign=0, unique=1; @@ -3646,55 +3629,40 @@ dragon4_scientific(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) } } + digit_mode = unique ? DigitMode_Unique : DigitMode_Exact; + if (unique == 0 && precision < 0) { PyErr_SetString(PyExc_TypeError, "in non-unique mode `precision` must be supplied"); return NULL; } - return Dragon4_Scientific(obj, unique, precision, sign, - trim, pad_left, exp_digits); + return Dragon4_Scientific(obj, digit_mode, precision, sign, trim, + pad_left, exp_digits); } /* * Prints floating-point scalars usign the Dragon4 algorithm, positional mode. - * Arguments: - * x - a numpy scalar of Floating type - * precision - number of fractional digits to show. In unique mode, can be - * ommited and the unique repr will be returned, otherwise the - * unique value will be truncated to this number of digits - * (breaking the uniqueness guarantee). In fixed mode, is - * required, and specifies the number of fractional digits to - * print. - * unique - whether to use unique (default) or fixed mode. - * sign - whether to show the sign for positive values. Default False - * trim - one of 'k', '.', '0', '-' to control trailing digits, as follows: - * k : don't trim zeros, always leave a decimal point - * . : trim all but the zero before the decimal point - * 0 : trim all trailing zeros, leave decimal point - * - : trim trailing zeros and a trailing decimal point - * Default is k. - * pad_left - pads left side of string with whitespace until at least - * this many characters are to the left of the decimal point. If - * -1, don't add any padding. Default -1. - * pad_right - pads right side of string with whitespace until at least - * this many characters are to the right of the decimal point. If - * -1, don't add any padding. Default -1. + * See docstring of `np.format_float_positional` for description of arguments. + * The differences is that a value of -1 is valid for pad_left, pad_right, + * precision, which is equivalent to `None`. */ static PyObject * dragon4_positional(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) { PyObject *obj; - static char *kwlist[] = {"x", "precision", "unique", "sign", "trim", - "pad_left", "pad_right", NULL}; + static char *kwlist[] = {"x", "precision", "unique", "fractional", + "sign", "trim", "pad_left", "pad_right", NULL}; int precision=-1, pad_left=-1, pad_right=-1; char *trimstr=NULL; + CutoffMode cutoff_mode; + DigitMode digit_mode; TrimMode trim = TrimMode_None; - int sign=0, unique=1; + int sign=0, unique=1, fractional=0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iiisii", kwlist, - &obj, &precision, &unique, &sign, &trimstr, &pad_left, - &pad_right)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iiiisii", kwlist, + &obj, &precision, &unique, &fractional, &sign, &trimstr, + &pad_left, &pad_right)) { return NULL; } @@ -3718,13 +3686,17 @@ dragon4_positional(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) } } + digit_mode = unique ? DigitMode_Unique : DigitMode_Exact; + cutoff_mode = fractional ? CutoffMode_FractionLength : + CutoffMode_TotalLength; + if (unique == 0 && precision < 0) { PyErr_SetString(PyExc_TypeError, "in non-unique mode `precision` must be supplied"); return NULL; } - return Dragon4_Positional(obj, unique, precision, sign, + return Dragon4_Positional(obj, digit_mode, cutoff_mode, precision, sign, trim, pad_left, pad_right); } @@ -3744,8 +3716,8 @@ format_longfloat(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) "not a longfloat"); return NULL; } - return Dragon4_Scientific(obj, precision, 0, 1, TrimMode_LeaveOneZero, - -1, -1); + return Dragon4_Scientific(obj, DigitMode_Unique, precision, 0, + TrimMode_LeaveOneZero, -1, -1); } static PyObject * diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src index a208c2f73..802d74dec 100644 --- a/numpy/core/src/multiarray/scalartypes.c.src +++ b/numpy/core/src/multiarray/scalartypes.c.src @@ -442,15 +442,18 @@ format_@name@(@type@ val, npy_bool scientific, int pad_left, int pad_right, int exp_digits) { if (scientific) { - return Dragon4_Scientific_AnySize(&val, sizeof(@type@), 1, precision, - sign, trim, pad_left, exp_digits); + return Dragon4_Scientific_AnySize(&val, sizeof(@type@), + DigitMode_Unique, precision, + sign, trim, pad_left, exp_digits); } else { - return Dragon4_Positional_AnySize(&val, sizeof(@type@), 1, precision, - sign, trim, pad_left, pad_right); + return Dragon4_Positional_AnySize(&val, sizeof(@type@), + DigitMode_Unique, CutoffMode_TotalLength, precision, + sign, trim, pad_left, pad_right); } } + /**end repeat**/ /* @@ -720,6 +723,7 @@ timedeltatype_str(PyObject *self) /**begin repeat * #kind = str, repr# + * #maxprec = 8, -1# */ /**begin repeat1 @@ -728,14 +732,15 @@ timedeltatype_str(PyObject *self) * #NAME = FLOAT, DOUBLE, LONGDOUBLE# */ +/* helper function choose scientific of fractional output, based on a cutoff */ static PyObject * @name@type_@kind@_either(npy_@name@ val, TrimMode trim_pos, TrimMode trim_sci, npy_bool sign) { - if (val < (npy_@name@)1.e16L) { - return format_@name@(val, 0, -1, sign, trim_pos, -1, -1, -1); + if (val < (npy_@name@)1.e10L) { + return format_@name@(val, 0, @maxprec@, sign, trim_pos, -1, -1, -1); } - return format_@name@(val, 1, -1, sign, trim_sci, -1, -1, -1); + return format_@name@(val, 1, @maxprec@, sign, trim_sci, -1, -1, -1); } static PyObject * diff --git a/numpy/core/tests/test_getlimits.py b/numpy/core/tests/test_getlimits.py index 361246c7f..455f5257c 100644 --- a/numpy/core/tests/test_getlimits.py +++ b/numpy/core/tests/test_getlimits.py @@ -80,7 +80,7 @@ class TestRepr(object): assert_equal(repr(np.iinfo(np.int16)), expected) def test_finfo_repr(self): - expected = "finfo(resolution=0.000001, min=-3.4028235e+38," + \ + expected = "finfo(resolution=1e-06, min=-3.4028235e+38," + \ " max=3.4028235e+38, dtype=float32)" assert_equal(repr(np.finfo(np.float32)), expected) |