diff options
| author | Yukihiro "Matz" Matsumoto <[email protected]> | 2013-03-18 06:52:13 -0700 |
|---|---|---|
| committer | Yukihiro "Matz" Matsumoto <[email protected]> | 2013-03-18 06:52:13 -0700 |
| commit | 215069d0d812f1eecd2c14ce8cab2fb639d55195 (patch) | |
| tree | 5917d049009b332061e1c64ec8d8d465551e3b78 /src/numeric.c | |
| parent | 816021043ec62181b0ea38842596b1da4c55bd60 (diff) | |
| parent | 9be437c6d5074ded76417792ab22b78eeb0351aa (diff) | |
| download | mruby-215069d0d812f1eecd2c14ce8cab2fb639d55195.tar.gz mruby-215069d0d812f1eecd2c14ce8cab2fb639d55195.zip | |
Merge pull request #1020 from monaka/pr-add-numeric-format-function
Add numeric format function
Diffstat (limited to 'src/numeric.c')
| -rw-r--r-- | src/numeric.c | 120 |
1 files changed, 99 insertions, 21 deletions
diff --git a/src/numeric.c b/src/numeric.c index 5560bbc7f..2801811c3 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -9,6 +9,7 @@ #include "mruby/string.h" #include "mruby/array.h" +#include <float.h> #include <math.h> #include <assert.h> #include <stdlib.h> @@ -17,10 +18,6 @@ #include <floatingpoint.h> #endif -#ifdef HAVE_FLOAT_H -#include <float.h> -#endif - #ifdef HAVE_IEEEFP_H #include <ieeefp.h> #endif @@ -166,6 +163,99 @@ num_abs(mrb_state *mrb, mrb_value num) * representation. */ +mrb_value +mrb_flo_to_str(mrb_state *mrb, mrb_float n, int max_digit) +{ + mrb_value result; + + if (max_digit > 40) { + mrb_raise(mrb, E_RANGE_ERROR, "Too large max_digit."); + } + + if (isnan(n)) { + result = mrb_str_new(mrb, "NaN", 3); + } + else if (isinf(n)) { + if (n < 0) { + result = mrb_str_new(mrb, "-inf", 4); + } + else { + result = mrb_str_new(mrb, "inf", 3); + } + } + else { + int digit; + int m; + int exp; + int e = 0; + char s[48]; + char *c = &s[0]; + + if (n < 0) { + n = -n; + *(c++) = '-'; + } + + exp = log10(n); + + if ((exp < 0 ? -exp : exp) > max_digit) { + /* exponent representation */ + e = 1; + m = exp; + if (m < 0) { + m -= 1; + } + n = n / pow(10.0, m); + m = 0; + } + else { + /* un-exponent (normal) representation */ + m = exp; + if (m < 0) { + m = 0; + } + } + + /* puts digits */ + while (max_digit >= 0) { + mrb_float weight = pow(10.0, m); + digit = floor(n / weight + FLT_EPSILON); + *(c++) = '0' + digit; + n -= (digit * weight); + max_digit--; + if (m-- == 0) { + *(c++) = '.'; + } + else if (m < -1 && n < FLT_EPSILON) { + break; + } + } + + if (e) { + *(c++) = 'e'; + if (exp > 0) { + *(c++) = '+'; + } else { + *(c++) = '-'; + exp = -exp; + } + + if (exp >= 100) { + mrb_raise(mrb, E_RANGE_ERROR, "Too large expornent."); + } + + *(c++) = '0' + exp / 10; + *(c++) = '0' + exp % 10; + } + + *c = '\0'; + + result = mrb_str_new(mrb, &s[0], c - &s[0]); + } + + return result; +} + /* 15.2.9.3.16(x) */ /* * call-seq: @@ -180,27 +270,15 @@ num_abs(mrb_state *mrb, mrb_value num) static mrb_value flo_to_s(mrb_state *mrb, mrb_value flt) { - char buf[32]; - int n; - - mrb_float value = mrb_float(flt); - - if (isinf(value)) { - static const char s[2][5] = { "-inf", "inf" }; - static const int n[] = { 4, 3 }; - int idx; - idx = (value < 0) ? 0 : 1; - return mrb_str_new(mrb, s[idx], n[idx]); - } else if(isnan(value)) - return mrb_str_new(mrb, "NaN", 3); + int max_digit; #ifdef MRB_USE_FLOAT - n = sprintf(buf, "%.7g", value); + max_digit = 7; #else - n = sprintf(buf, "%.14g", value); + max_digit = 14; #endif - assert(n >= 0); - return mrb_str_new(mrb, buf, n); + + return mrb_flo_to_str(mrb, mrb_float(flt), max_digit); } /* 15.2.9.3.2 */ |
