mirror of
https://github.com/justinethier/cyclone.git
synced 2025-07-11 23:07:36 +02:00
Merge pull request #516 from justinethier/issue-513-parsing-of-rationals
Issue 513 parsing of rationals
This commit is contained in:
commit
6c04ce4ca4
3 changed files with 73 additions and 4 deletions
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
## 0.36.0 - TBD
|
## 0.36.0 - TBD
|
||||||
|
|
||||||
|
Features
|
||||||
|
|
||||||
|
- Enhanced the reader to parse rationals and store them as inexact numbers.
|
||||||
|
|
||||||
Bug Fixes
|
Bug Fixes
|
||||||
|
|
||||||
- Fix `exact` to properly handle complex numbers, including raising an error when passed `nan` or `inf` double values.
|
- Fix `exact` to properly handle complex numbers, including raising an error when passed `nan` or `inf` double values.
|
||||||
|
|
68
runtime.c
68
runtime.c
|
@ -2557,7 +2557,8 @@ typedef enum {
|
||||||
STR2INT_SUCCESS,
|
STR2INT_SUCCESS,
|
||||||
STR2INT_OVERFLOW,
|
STR2INT_OVERFLOW,
|
||||||
STR2INT_UNDERFLOW,
|
STR2INT_UNDERFLOW,
|
||||||
STR2INT_INCONVERTIBLE
|
STR2INT_INCONVERTIBLE,
|
||||||
|
STR2INT_RATIONAL
|
||||||
} str2int_errno;
|
} str2int_errno;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2588,12 +2589,18 @@ static str2int_errno str2int(int *out, char *s, int base)
|
||||||
errno = 0;
|
errno = 0;
|
||||||
long l = strtol(s, &end, base);
|
long l = strtol(s, &end, base);
|
||||||
/* Both checks are needed because INT_MAX == LONG_MAX is possible. */
|
/* Both checks are needed because INT_MAX == LONG_MAX is possible. */
|
||||||
if (l > CYC_FIXNUM_MAX /*INT_MAX*/ || (errno == ERANGE && l == LONG_MAX))
|
if (l > CYC_FIXNUM_MAX /*INT_MAX*/ || (errno == ERANGE && l == LONG_MAX)) {
|
||||||
return STR2INT_OVERFLOW;
|
return STR2INT_OVERFLOW;
|
||||||
if (l < CYC_FIXNUM_MIN /*INT_MIN*/ || (errno == ERANGE && l == LONG_MIN))
|
}
|
||||||
|
if (l < CYC_FIXNUM_MIN /*INT_MIN*/ || (errno == ERANGE && l == LONG_MIN)) {
|
||||||
return STR2INT_UNDERFLOW;
|
return STR2INT_UNDERFLOW;
|
||||||
if (*end != '\0')
|
}
|
||||||
|
if (*end == '/') {
|
||||||
|
return STR2INT_RATIONAL;
|
||||||
|
}
|
||||||
|
if (*end != '\0') {
|
||||||
return STR2INT_INCONVERTIBLE;
|
return STR2INT_INCONVERTIBLE;
|
||||||
|
}
|
||||||
*out = l;
|
*out = l;
|
||||||
return STR2INT_SUCCESS;
|
return STR2INT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -2610,6 +2617,51 @@ int str_is_bignum(str2int_errno errnum, char *c)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read a rational number from given string.
|
||||||
|
* @param data Thread data object for the caller.
|
||||||
|
* @param char* String to read
|
||||||
|
* @return double Return number as double, since cyclone does
|
||||||
|
* not support a rational number type at this time
|
||||||
|
*/
|
||||||
|
double string2rational(void *data, char *s)
|
||||||
|
{
|
||||||
|
// Duplicate string so we can safely create separate strings
|
||||||
|
// for numerator and denominator
|
||||||
|
char *nom = _strdup(s);
|
||||||
|
if (nom == NULL) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *denom = strchr(nom, '/');
|
||||||
|
if (denom == NULL) {
|
||||||
|
// Should never happen since we check for '/' elsewhere
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
denom[0] = '\0';
|
||||||
|
denom++;
|
||||||
|
|
||||||
|
// Parse both rational components as bignums since
|
||||||
|
// that code handles any integer
|
||||||
|
alloc_bignum(data, bn_nom);
|
||||||
|
if (MP_OKAY != mp_read_radix(&(bignum_value(bn_nom)), nom, 10)) {
|
||||||
|
Cyc_rt_raise2(data, "Error converting string to bignum", nom);
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_bignum(data, bn_denom);
|
||||||
|
if (MP_OKAY != mp_read_radix(&(bignum_value(bn_denom)), denom, 10)) {
|
||||||
|
Cyc_rt_raise2(data, "Error converting string to bignum", denom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent memory leak
|
||||||
|
free(nom);
|
||||||
|
|
||||||
|
// Compute final result as double
|
||||||
|
double x = mp_get_double(&bignum_value(bn_nom));
|
||||||
|
double y = mp_get_double(&bignum_value(bn_denom));
|
||||||
|
return x / y;
|
||||||
|
}
|
||||||
|
|
||||||
object Cyc_string2number_(void *data, object cont, object str)
|
object Cyc_string2number_(void *data, object cont, object str)
|
||||||
{
|
{
|
||||||
int result, rv;
|
int result, rv;
|
||||||
|
@ -2622,6 +2674,14 @@ object Cyc_string2number_(void *data, object cont, object str)
|
||||||
rv = str2int(&result, s, 10);
|
rv = str2int(&result, s, 10);
|
||||||
if (rv == STR2INT_SUCCESS) {
|
if (rv == STR2INT_SUCCESS) {
|
||||||
_return_closcall1(data, cont, obj_int2obj(result));
|
_return_closcall1(data, cont, obj_int2obj(result));
|
||||||
|
} else if (rv == STR2INT_RATIONAL ||
|
||||||
|
// Could still be a rational if numerator is
|
||||||
|
// bignum, so in that case do one more scan
|
||||||
|
((rv == STR2INT_OVERFLOW || rv == STR2INT_UNDERFLOW) &&
|
||||||
|
strchr(s, '/') != NULL)) {
|
||||||
|
double d = string2rational(data, s);
|
||||||
|
make_double(result, d);
|
||||||
|
_return_closcall1(data, cont, &result);
|
||||||
} else if (str_is_bignum(rv, s)) {
|
} else if (str_is_bignum(rv, s)) {
|
||||||
alloc_bignum(data, bn);
|
alloc_bignum(data, bn);
|
||||||
if (MP_OKAY != mp_read_radix(&(bignum_value(bn)), s, 10)) {
|
if (MP_OKAY != mp_read_radix(&(bignum_value(bn)), s, 10)) {
|
||||||
|
|
|
@ -48,6 +48,11 @@
|
||||||
"rationals"
|
"rationals"
|
||||||
(test 3.0 (numerator (/ 6 4)))
|
(test 3.0 (numerator (/ 6 4)))
|
||||||
(test 2.0 (denominator (/ 6 4)))
|
(test 2.0 (denominator (/ 6 4)))
|
||||||
|
(test 3.0 (expt 81 1/4))
|
||||||
|
(test #t
|
||||||
|
(< 1.0e+40
|
||||||
|
(/ 33333333333333333333333333333333333333333 3.0)
|
||||||
|
1.2e+40))
|
||||||
)
|
)
|
||||||
|
|
||||||
(test-group
|
(test-group
|
||||||
|
|
Loading…
Add table
Reference in a new issue