#148: first attempt to write with an arbitrary base

This commit is contained in:
Louis Rubet 2017-06-27 17:19:16 +02:00
parent 8648d69554
commit 59875e360b
3 changed files with 213 additions and 6 deletions

View file

@ -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

View file

@ -1,6 +1,8 @@
#include <string>
#include <math.h>
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, "<unknown number representation>");
break;
}
break;
case cmd_complex:

View file

@ -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;