void qore_number_private::applyRoundingHeuristic(QoreString& str, qore_size_t dp, qore_size_t last) { // if there are some significant digits after the decimal point (signal) bool signal = false; // the position of the last significant digit qore_offset_t pos = (qore_offset_t)dp; qore_size_t i = dp; // the last digit found in the sequence char lc = 0; // 0 or 9 count unsigned cnt = 0; bool has_e = (str[last] == 'e'); // don't check the last character --last; // check all except the last digit while (i < last) { char c = str[i++]; if (c == '0' || c == '9') { // continue the sequence if (c == lc) { ++cnt; continue; } // check for 2nd threshold if ((i == last) && cnt > QORE_MPFR_ROUND_THRESHOLD_2) { break; } // set last digit to digit found lc = c; // if first digit, then do not set signal flag if (i == (dp + 1)) continue; } else { // check for 2nd threshold if ((i == last) && cnt > QORE_MPFR_ROUND_THRESHOLD_2) { break; } // no 0 or 9 digit found lc = 0; } // mark position of the last significant digit pos = i - 2; //printd(5, "qore_number_private::applyRoundingHeuristic('%s') set pos: %lld ('%c') dp: %lld\n", str.getBuffer(), pos, str[pos], dp); // found a non-noise digit if (!signal) signal = true; // reset count cnt = 0; } // round the number for display if (signal && cnt > QORE_MPFR_ROUND_THRESHOLD) { //printd(5, "ROUND BEFORE: (pos: %d dp: %d cnt: %d has_e: %d e: %c) %s\n", pos, dp, cnt, has_e, has_e ? str[pos + cnt + 4] : 'x', str.getBuffer()); // if rounding right after the decimal point, then remove the decimal point if (pos == (qore_offset_t)dp) --pos; if (has_e && str[pos + cnt + 3] == 'e') --cnt; // remove the excess digits if (!has_e) str.terminate(pos + 1); else str.replace(pos + 1, cnt + 3, (const char*)0); // rounding down is easy; the truncation is enough if (lc == '9') // round up roundUp(str, pos); //printd(5, "ROUND AFTER: %s\n", str.getBuffer()); } }
int qore_number_private::formatNumberString(QoreString& num, const QoreString& fmt, ExceptionSink* xsink) { assert(!num.empty()); assert(num.getEncoding() == fmt.getEncoding()); // get the length of the format string in characters (not bytes) qore_size_t fl = fmt.length(); if (fmt.empty() || fl == 2) { printd(5, "qore_number_private::formatNumberString() invalid format string: '%s' for number: '%s'\n", fmt.getBuffer(), num.getBuffer()); return 0; } // get thousands separator character QoreString tsep; if (tsep.concat(fmt, 0, 1, xsink)) return -1; // decimal separator QoreString dsep; // number of digits after the decimal separator unsigned prec = 0; if (fl > 1) { if (dsep.concat(fmt, 1, 1, xsink)) return -1; // get byte offset of start of decimal precision number qore_offset_t i = fmt.getByteOffset(2, xsink); if (*xsink) return -1; assert(i >= 2); prec = atoi(fmt.getBuffer() + i); if (!prec) dsep.clear(); } //printd(5, "qore_number_private::formatNumberString() tsep: '%s' dsep: '%s' prec: %d '%s'\n", tsep.getBuffer(), dsep.getBuffer(), prec, num.getBuffer()); // find decimal point qore_offset_t dp = num.find('.'); if (dp != -1) { // how many digits do we have now after the decimal point qore_size_t d = num.strlen() - dp - 1; assert(d); if (d < prec) num.addch('0', prec - d); else if (d > prec) { if ((num[dp + prec + 1] > '4') && (roundUp(num, dp + prec))) ++dp; num.terminate(dp + prec + 1); } // now substitute decimal point if necessary if (dsep.strlen() != 1 || dsep[0] != '.') num.replace(dp, 1, dsep.getBuffer()); } else { dp = num.size(); if (prec) { // add decimal point num.concat(&dsep, xsink); assert(!*xsink); // add zeros for significant digits num.addch('0', prec); } } // now insert thousands separator // start of digits before the decimal point qore_offset_t ds = num[0] == '-' ? 1 : 0; // work backwards from the decimal point qore_offset_t i = dp - 3; while (i > ds) { num.replace(i, 0, tsep.getBuffer()); i -= 3; } //printd(0, "qore_number_private::formatNumberString() ok '%s'\n", num.getBuffer()); //assert(false); xxx return 0; }