diff --git a/src/core/float_to_digits.cc b/src/core/float_to_digits.cc index 58a0338827..803912f262 100644 --- a/src/core/float_to_digits.cc +++ b/src/core/float_to_digits.cc @@ -23,19 +23,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* -^- */ -/* -*- mode: c; c-basic-offset: 8 -*- */ -/* - Copyright (c) 2010, Juan Jose Garcia Ripoll. - Copyright (c) 2012, Christian E. Schafmeister - - ECL is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - See file '../../Copyright' for full details. -*/ #include #include @@ -49,174 +36,34 @@ THE SOFTWARE. namespace core { -#define PRINT_BASE clasp_make_fixnum(10) -#define EXPT_RADIX(x) clasp_ash(clasp_make_fixnum(1), x) - -typedef struct { - Real_sp r; - Real_sp s; - Real_sp mm; - Real_sp mp; - bool high_ok; - bool low_ok; -} float_approx; - -Real_sp times2(Real_sp x) { return gc::As(clasp_plus(x, x)); } - -static float_approx* setup(Float_sp number, float_approx* approx) { - Real_mv mv_f = cl__integer_decode_float(number); - Integer_sp f = gc::As(mv_f); - MultipleValues& mvn = core::lisp_multipleValues(); - Fixnum_sp fne = gc::As(mvn.valueGet(1, mv_f.number_of_values())); - Fixnum e = fne.unsafe_fixnum(), min_e; - bool limit_f = 0; - switch (clasp_t_of(number)) { - case number_SingleFloat: - min_e = FLT_MIN_EXP; - limit_f = (unbox_single_float(gc::As(number)) == ldexpf(FLT_RADIX, FLT_MANT_DIG - 1)); - break; - case number_DoubleFloat: - min_e = DBL_MIN_EXP; - limit_f = (gc::As(number)->get() == ldexp(FLT_RADIX, DBL_MANT_DIG - 1)); - break; -#ifdef CLASP_LONG_FLOAT - case number_LongFloat: - min_e = LDBL_MIN_EXP; - limit_f = (number.as()->get() == ldexpl(FLT_RADIX, LDBL_MANT_DIG - 1)); -#endif - default: - SIMPLE_ERROR("Illegal type"); - } - approx->low_ok = approx->high_ok = clasp_evenp(f); - if (e > 0) { - Real_sp be = EXPT_RADIX(e); - if (limit_f) { - Real_sp be1 = gc::As(clasp_times(be, clasp_make_fixnum(FLT_RADIX))); - approx->r = times2(gc::As(clasp_times(f, be1))); - approx->s = clasp_make_fixnum(FLT_RADIX * 2); - approx->mm = be; - approx->mp = be1; - } else { - approx->r = times2(gc::As(clasp_times(f, be))); - approx->s = clasp_make_fixnum(2); - approx->mm = be; - approx->mp = be; - } - } else if (!limit_f || (e == min_e)) { - approx->r = times2(f); - approx->s = times2(EXPT_RADIX(-e)); - approx->mp = clasp_make_fixnum(1); - approx->mm = clasp_make_fixnum(1); - } else { - approx->r = times2(clasp_make_fixnum(FLT_RADIX)); - approx->s = times2(EXPT_RADIX(1 - e)); - approx->mp = clasp_make_fixnum(FLT_RADIX); - approx->mm = clasp_make_fixnum(1); - } - return approx; -} +template T_mv float_to_digits(T_sp tdigits, T number, T_sp round_position, T_sp relativep) { + using significand_type = typename fmt::detail::dragonbox::decimal_fp::significand_type; -static Fixnum scale(float_approx* approx) { - Fixnum k = 0; - Real_sp x = gc::As(clasp_plus(approx->r, approx->mp)); - int sign; - do { - sign = clasp_number_compare(x, approx->s); - if (approx->high_ok) { - if (sign < 0) - break; - } else { - if (sign <= 0) - break; - } - approx->s = gc::As(clasp_times(approx->s, PRINT_BASE)); - k++; - } while (1); - do { - x = gc::As(clasp_times(x, PRINT_BASE)); - sign = clasp_number_compare(x, approx->s); - if (approx->high_ok) { - if (sign >= 0) - break; - } else { - if (sign > 0) - break; - } - k--; - approx->r = gc::As(clasp_times(approx->r, PRINT_BASE)); - approx->mm = gc::As(clasp_times(approx->mm, PRINT_BASE)); - approx->mp = gc::As(clasp_times(approx->mp, PRINT_BASE)); - } while (1); - return k; -} + StrNs_sp digits = tdigits.nilp() ? gc::As(core__make_vector(cl::_sym_base_char, 10, true, clasp_make_fixnum(0))) + : gc::As(tdigits); + auto decimal = fmt::detail::dragonbox::to_decimal(number); + int digit_count = fmt::detail::count_digits(decimal.significand); + auto position = decimal.exponent + digit_count; -static StrNs_sp generate(StrNs_sp digits, float_approx* approx) { - Real_sp d, x; - gctools::Fixnum digit; - bool tc1, tc2; - MultipleValues& mvn = core::lisp_multipleValues(); - do { - Real_mv mv_d = clasp_truncate2(gc::As(clasp_times(approx->r, PRINT_BASE)), approx->s); - d = mv_d; - approx->r = gc::As(mvn.valueGet(1, mv_d.number_of_values())); - approx->mp = gc::As(clasp_times(approx->mp, PRINT_BASE)); - approx->mm = gc::As(clasp_times(approx->mm, PRINT_BASE)); - tc1 = approx->low_ok ? clasp_lowereq(approx->r, approx->mm) : clasp_lower(approx->r, approx->mm); - x = gc::As(clasp_plus(approx->r, approx->mp)); - tc2 = approx->high_ok ? clasp_greatereq(x, approx->s) : clasp_greater(x, approx->s); - if (tc1 || tc2) { - break; - } - digits->vectorPushExtend(clasp_make_character(clasp_digit_char(gc::As(d).unsafe_fixnum(), 10))); - } while (1); - if (tc2 && !tc1) { - digit = clasp_safe_fixnum(d) + 1; - } else if (tc1 && !tc2) { - digit = clasp_safe_fixnum(d); - } else if (clasp_lower(times2(approx->r), approx->s)) { - digit = clasp_safe_fixnum(d); - } else { - digit = clasp_safe_fixnum(d) + 1; - } - digits->vectorPushExtend(clasp_make_character(clasp_digit_char(digit, 10))); - return digits; -} + if (round_position.notnilp()) { + int pos = gc::As(round_position).unsafe_fixnum(); + pos = relativep.nilp() ? (position - pos) : (pos + 1); -static void change_precision(float_approx* approx, T_sp tposition, T_sp relativep) { - if (tposition.nilp()) - return; - gctools::Fixnum pos; - Fixnum_sp position = gc::As(tposition); - pos = position.unsafe_fixnum(); - if (!relativep.nilp()) { - Real_sp k = clasp_make_fixnum(0); - Real_sp l = clasp_make_fixnum(1); - while (clasp_lower(clasp_times(approx->s, l), clasp_plus(approx->r, approx->mp))) { - k = gc::As(clasp_one_plus(k)); - l = gc::As(clasp_times(l, PRINT_BASE)); - } - position = gc::As(clasp_minus(k, position)); - { - Real_sp e1 = gc::As(cl__expt(PRINT_BASE, position)); - Real_sp e2 = gc::As(clasp_divide(e1, clasp_make_fixnum(2))); - if (clasp_greatereq(clasp_plus(approx->r, clasp_times(approx->s, e1)), clasp_times(approx->s, e2))) - position = gc::As(clasp_one_minus(position)); + if (pos < 0) { + position -= pos; + pos = 0; } - } - { - Real_sp x = gc::As(clasp_times(approx->s, cl__expt(PRINT_BASE, position))); - Real_sp e = gc::As(clasp_divide(x, clasp_make_fixnum(2))); - Real_sp low = clasp_max2(approx->mm, e); - Real_sp high = clasp_max2(approx->mp, e); - if (clasp_lowereq(approx->mm, low)) { - approx->mm = low; - approx->low_ok = 1; - } - if (clasp_lowereq(approx->mp, high)) { - approx->mp = high; - approx->high_ok = 1; + + if (pos < digit_count) { + significand_type divisor = std::pow(10, digit_count - pos); + decimal.significand = (decimal.significand + (divisor / 2)) / divisor; } } + + for (auto ch : std::to_string(decimal.significand)) + digits->vectorPushExtend(clasp_make_character(ch), 64); + + return Values(clasp_make_fixnum((decimal.significand == 0) ? 0 : position), digits); } CL_LAMBDA(digits number position relativep); @@ -225,20 +72,20 @@ CL_DOCSTRING(R"dx(float_to_digits)dx"); DOCGROUP(clasp); CL_DEFUN T_mv core__float_to_digits(T_sp tdigits, Float_sp number, T_sp position, T_sp relativep) { ASSERT(tdigits.nilp() || gc::IsA(tdigits)); - gctools::Fixnum k; - float_approx approx[1]; - setup(number, approx); - change_precision(approx, position, relativep); - k = scale(approx); - StrNs_sp digits; - if (tdigits.nilp()) { - digits = - gc::As(core__make_vector(cl::_sym_base_char, 10, true /* adjustable */, clasp_make_fixnum(0) /* fill pointer */)); - } else { - digits = gc::As(tdigits); + + switch (clasp_t_of(number)) { + case number_SingleFloat: + return float_to_digits(tdigits, unbox_single_float(gc::As(number)), position, relativep); + case number_DoubleFloat: + return float_to_digits(tdigits, gc::As(number)->get(), position, relativep); + break; +#ifdef CLASP_LONG_FLOAT + case number_LongFloat: + return float_to_digits(tdigits, gc::As(number)->get(), position, relativep); +#endif + default: + SIMPLE_ERROR("Illegal type"); } - generate(digits, approx); - return Values(clasp_make_fixnum(k), digits); } SYMBOL_EXPORT_SC_(CorePkg, float_to_digits); diff --git a/src/core/float_to_string.cc b/src/core/float_to_string.cc index 958370afe4..e853a5067e 100644 --- a/src/core/float_to_string.cc +++ b/src/core/float_to_string.cc @@ -106,6 +106,8 @@ T_sp core_float_to_string_free(Float_sp number, Number_sp e_min, Number_sp e_max /* Do we have to print in exponent notation? */ if (clasp_lowereq(exp, e_min) || clasp_lowereq(e_max, exp)) { insert_char(buffer, base + 1, '.'); + if (gc::As(buffer)->fillPointer() == base + 2) + buffer->vectorPushExtend(clasp_make_character('0')); print_float_exponent(buffer, number, e - 1); } else if (e > 0) { gc::Fixnum l = gc::As(buffer)->fillPointer() - base; diff --git a/src/lisp/kernel/lsp/format.lisp b/src/lisp/kernel/lsp/format.lisp index 549cd8508e..1ff131f267 100644 --- a/src/lisp/kernel/lsp/format.lisp +++ b/src/lisp/kernel/lsp/format.lisp @@ -1374,14 +1374,27 @@ nil))) (if (and w ovf e (> elen e)) ;exponent overflow (dotimes (i w) (write-char ovf stream)) - (multiple-value-bind (fstr flen lpoint) + (multiple-value-bind (fstr flen lpoint tpoint dpos) (sys::flonum-to-string number spaceleft fdig (- expt) fmin) + (when (and (plusp k) + (< k dpos)) + (incf expt (- dpos k)) + (setf estr (decimal-string (abs expt)) + tpoint nil) + (loop for pos from dpos downto k + do (setf (char fstr pos) (if (= pos k) #\. (char fstr (1- pos)))))) + (when (eql fdig 0) + (setq tpoint nil)) (when w (decf spaceleft flen) (when lpoint (if (> spaceleft 0) (decf spaceleft) - (setq lpoint nil)))) + (setq lpoint nil))) + (when tpoint + (if (> spaceleft 0) + (decf spaceleft) + (setq tpoint nil)))) (cond ((and w (< spaceleft 0) ovf) ;;significand overflow (dotimes (i w) (write-char ovf stream))) @@ -1392,6 +1405,7 @@ (if atsign (write-char #\+ stream))) (when lpoint (write-char #\0 stream)) (write-string fstr stream) + (when tpoint (write-char #\0 stream)) (write-char (if marker marker (format-exponent-marker number)) diff --git a/tools-for-build/ansi-test-expected-failures.sexp b/tools-for-build/ansi-test-expected-failures.sexp index b6552ed98c..16e0827703 100644 --- a/tools-for-build/ansi-test-expected-failures.sexp +++ b/tools-for-build/ansi-test-expected-failures.sexp @@ -87,7 +87,6 @@ DEFCLASS.FORWARD-REF.3 DEFSTRUCT.ERROR.3 DEFSTRUCT.ERROR.4 ALL-EXPORTED-CL-CLASS-NAMES-ARE-VALID -FORMAT.E.6 FORMAT.E.26 DEFINE-METHOD-COMBINATION-LONG.05.2