diff --git a/src/constant.h b/src/constant.h index 2b93b0b..a039e7e 100644 --- a/src/constant.h +++ b/src/constant.h @@ -76,4 +76,35 @@ typedef enum { #define HISTORY_FILE ".rpn_history" #define HISTORY_FILE_MAX_LINES (100) +// some defs for mpfr +#if _MPFR_EXP_FORMAT == 1 +#define MPFR_EXP_MAX (SHRT_MAX) +#define MPFR_EXP_MIN (SHRT_MIN) +#elif _MPFR_EXP_FORMAT == 2 +#define MPFR_EXP_MAX (INT_MAX) +#define MPFR_EXP_MIN (INT_MIN) +#elif _MPFR_EXP_FORMAT == 3 +#define MPFR_EXP_MAX (LONG_MAX) +#define MPFR_EXP_MIN (LONG_MIN) +#elif _MPFR_EXP_FORMAT == 4 +#define MPFR_EXP_MAX (MPFR_INTMAX_MAX) +#define MPFR_EXP_MIN (MPFR_INTMAX_MIN) +#else +#error "Invalid MPFR Exp format" +#endif + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(h,i) ((h) > (i) ? (h) : (i)) + +#define MPFR_EXP_INF (MPFR_EXP_MIN+3) +#define MPFR_EXP_NAN (MPFR_EXP_MIN+2) +#define MPFR_EXP(x) ((x)->_mpfr_exp) +#define MPFR_IS_SINGULAR(x) (MPFR_EXP(x) <= MPFR_EXP_INF) +#define MPFR_UNLIKELY(x) (__builtin_expect(!!(x),0)) +#define MPFR_IS_NAN(x) (MPFR_EXP(x) == MPFR_EXP_NAN) +#define MPFR_IS_INF(x) (MPFR_EXP(x) == MPFR_EXP_INF) +#define MPFR_IS_NEG(x) (MPFR_SIGN(x) < 0) +#define MPFR_IS_POS(x) (MPFR_SIGN(x) > 0) +#define MPFR_PREC(x) ((x)->_mpfr_prec) + #endif diff --git a/src/object.cpp b/src/object.cpp index d87e6c5..89a22ac 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -1,6 +1,8 @@ #include +#include using namespace std; +#include "mpfr.h" #include "constant.h" #include "object.hpp" @@ -18,21 +20,195 @@ string number::s_mpfr_printf_format = string(MPFR_DEFAULT_FORMAT); // const char* object::s_cmd_type_string[cmd_max] = CMD_TYPE_STRINGS; +static bool is_min(mpfr_t p, mpfr_prec_t prec) +{ + // see mpfr_vasprintf code + bool ret; + int round_away; + switch (floating_t::s_mpfr_rnd) + { + case MPFR_RNDA: + round_away = 1; + break; + case MPFR_RNDD: + round_away = MPFR_IS_NEG (p); + break; + case MPFR_RNDU: + round_away = MPFR_IS_POS (p); + break; + case MPFR_RNDN: + { + /* compare |p| to y = 0.5*10^(-prec) */ + mpfr_t y; + mpfr_exp_t e = MAX (MPFR_PREC (p), 56); + mpfr_init2 (y, e + 8); + do + { + /* find a lower approximation of + 0.5*10^(-prec) different from |p| */ + e += 8; + mpfr_set_prec (y, e); + mpfr_set_si (y, -prec, MPFR_RNDN); + mpfr_exp10 (y, y, MPFR_RNDD); + mpfr_div_2ui (y, y, 1, MPFR_RNDN); + } while (mpfr_cmpabs (y, p) == 0); + + round_away = mpfr_cmpabs (y, p) < 0; + mpfr_clear (y); + } + break; + default: + round_away = 0; + } + + if (round_away) + /* round away from zero: the last output digit is '1' */ + ret = true; + else + /* only zeros in fractional part */ + ret = false; + return ret; +} + +static int base_digits_from_bit_precision(int base, int bit_precision) +{ + return (int)ceil(bit_precision * log(2.0) / log((double)base)); +} + +static void print_fix(FILE* stream, mpfr_t real, int base) +{ + // see mpfr_vasprintf code + mpfr_exp_t exp = mpfr_floor_logn(real, base); + int digits = number::s_decimal_digits; + int i; + + if (MPFR_UNLIKELY(MPFR_IS_SINGULAR(real))) + { + if (MPFR_IS_NAN(real)) + fputs("nan", stream); + else if (MPFR_IS_INF(real)) + { + if (MPFR_IS_NEG(real)) + fputc('-', stream); + fputs("inf", stream); + } + else + { + // zero + if (MPFR_IS_NEG(real)) + fputc('-', stream);//signed zero is allowed + fputc('0', stream); + if (number::s_decimal_digits > 0) + { + fputc('.', stream); + for(i = 0; i < digits; i++) + fputc('0', stream); + } + } + } + else if (exp < -number::s_decimal_digits) + { + if (MPFR_IS_NEG(real)) + fputc('-', stream); + fputc('0', stream); + if (number::s_decimal_digits > 0) + { + fputc('.', stream); + for (i = 0; i< digits - 1; i++) + fputc('0', stream); + + if (is_min(real, digits)) + fputc('1', stream); + else + fputc('0', stream); + } + } + else + { + char* str = mpfr_get_str(NULL, &exp, base, digits + exp + 1, real, floating_t::s_mpfr_rnd); + int len = strlen(str); + + if (len > 0) + { + if (str[0] == '-') + { + fputc(str[0], stream); + len--; + str++; + } + else if (str[0] == '+') + { + len--; + str++; + } + if (exp < 0) + { + fputc('0', stream); + if (number::s_decimal_digits > 0) + { + fputc('.', stream); + for (i = 0; i < -(int)exp; i++) + fputc('0', stream); + fputs(str, stream); + for (i = 0; i < (int)(number::s_decimal_digits - len + exp); i++) + fputc('0', stream); + } + } + else + { + if (exp == 0) + fputc('0', stream); + else + for (i = 0; i < (int)exp; i++) + fputc(str[i], stream); + if (number::s_decimal_digits > 0) + { + fputc('.', stream); + + int remaining = (int)MIN(strlen(str) - exp - 1, digits) + 1; + for (i = (int)exp; i < remaining + (int)exp; i++) + fputc(str[i], stream); + for (i = 0; i < (int)(exp + digits - len); i++) + fputc('0', stream); + } + } + } + } +} + void object::show(FILE* stream) { + int digits; + char* str; + switch(_type) { case cmd_number: + switch(number::s_mode) + { + case number::fix: + //printf("OLD: "); + //mpfr_fprintf(stream, number::s_mpfr_printf_format.c_str(), ((number*)this)->_value.mpfr); + print_fix(stream, ((number*)this)->_value.mpfr, 2); + break; + } switch(((number*)this)->_representation) { case number::dec: mpfr_fprintf(stream, number::s_mpfr_printf_format.c_str(), ((number*)this)->_value.mpfr); break; case number::hex: - mpfr_fprintf(stream, string(MPFR_FORMAT_HEX).c_str(), ((number*)this)->_value.mpfr); + fprintf(stream, "0x"); + print_fix(stream, ((number*)this)->_value.mpfr, 16); + //mpfr_fprintf(stream, string(MPFR_FORMAT_HEX).c_str(), ((number*)this)->_value.mpfr); + break; + case number::bin: + fprintf(stream, "0b"); + print_fix(stream, ((number*)this)->_value.mpfr, 2); break; default: fprintf(stream, ""); + break; } break; case cmd_complex: diff --git a/src/rpn-general.hpp b/src/rpn-general.hpp index 5e6b5f4..969e3ed 100644 --- a/src/rpn-general.hpp +++ b/src/rpn-general.hpp @@ -69,14 +69,14 @@ string make_digit_format(int decimal_digits, const char* printf_format) return ss.str(); } -bool check_precision_inbound(double precision) +bool check_decimal_digits(double precision) { bool ret = true; // MPFR_PREC_MAX mpfr_prec_t depends on _MPFR_PREC_FORMAT macro (see mpfr.h) // this could not exceed 63 bits max (0x7FFFFFFFFFFFFFFF) double prec_max = (double)MPFR_PREC_MAX; - double prec_min = (double)MPFR_PREC_MIN; + double prec_min = 0.0; if (precision < prec_min || precision > prec_max) ret = false; @@ -88,7 +88,7 @@ void std() { // to std mode number::s_mode = number::std; - + // calc max nb of digits user can see with the current bit precision number::s_decimal_digits = decimal_digits_from_bit_precision(floating_t::s_mpfr_prec); number::s_mpfr_printf_format = make_digit_format(number::s_decimal_digits, MPFR_FORMAT_STD); @@ -101,7 +101,7 @@ void fix() double precision = double(((number*)_stack->pop_back())->_value); - if (check_precision_inbound(precision)) + if (check_decimal_digits(precision)) { // set mode, precision, decimal digits and print format number::s_mode = number::fix; @@ -119,7 +119,7 @@ void sci() double precision = double(((number*)_stack->pop_back())->_value); - if (check_precision_inbound(precision)) + if (check_decimal_digits(precision)) { // set mode, precision, decimal digits and print format number::s_mode = number::sci;