mruby-random: Random#rand(nil) should raise TypeError
[mruby.git] / src / readfloat.c
dissimilarity index 82%
index a89fcaf..856e7fa 100644 (file)
-#include <mruby.h>
-
-#ifndef MRB_NO_FLOAT
-/*
- * strtod implementation.
- * author: Yasuhiro Matsumoto (@mattn)
- * license: public domain
- */
-
-/*
-The original code can be found in https://github.com/mattn/strtod
-
-I modified the routine for mruby:
-
- * renamed the function `vim_strtod` -> `mrb_read_float`
- * simplified the code
- * changed the API
-
-My modifications in this file are also placed in the public domain.
-
-Matz (Yukihiro Matsumoto)
-*/
-
-#include <string.h>
-#include <math.h>
-
-MRB_API mrb_bool
-mrb_read_float(const char *str, char **endp, double *fp)
-{
-  double d = 0.0;
-  int sign;
-  int n = 0;
-  const char *p, *a;
-
-  a = p = str;
-  while (ISSPACE(*p))
-    p++;
-
-  /* decimal part */
-  sign = 1;
-  if (*p == '-') {
-    sign = -1;
-    p++;
-  }
-  else if (*p == '+')
-    p++;
-  if (ISDIGIT(*p)) {
-    d = (double)(*p++ - '0');
-    while (*p && ISDIGIT(*p)) {
-      d = d * 10.0 + (double)(*p - '0');
-      p++;
-      n++;
-    }
-    a = p;
-  }
-  else if (*p != '.')
-    goto done;
-  d *= sign;
-
-  /* fraction part */
-  if (*p == '.') {
-    double f = 0.0;
-    double base = 0.1;
-    p++;
-
-    if (ISDIGIT(*p)) {
-      while (*p && ISDIGIT(*p)) {
-        f += base * (*p - '0') ;
-        base /= 10.0;
-        p++;
-        n++;
-      }
-    }
-    d += f * sign;
-    a = p;
-  }
-
-  /* exponential part */
-  if ((*p == 'E') || (*p == 'e')) {
-    int e = 0;
-
-    p++;
-    sign = 1;
-    if (*p == '-') {
-      sign = -1;
-      p++;
-    }
-    else if (*p == '+')
-      p++;
-
-    if (ISDIGIT(*p)) {
-      while (*p == '0')
-        p++;
-      if (*p == '\0') --p;
-      e = (int)(*p++ - '0');
-      for (; *p && ISDIGIT(*p); p++) {
-        if (e < 10000)
-          e = e * 10 + (*p - '0');
-      }
-      e *= sign;
-    }
-    else if (!ISDIGIT(*(a-1))) {
-      return FALSE;
-    }
-    else if (*p == 0)
-      goto done;
-    d *= pow(10.0, (double)e);
-    a = p;
-  }
-  else if (p > str && !ISDIGIT(*(p-1))) {
-    goto done;
-  }
-
-done:
-  *fp = d;
-  if (endp) *endp = (char*)a;
-  if (str == a) return FALSE;
-  return TRUE;
-}
-#endif
+#include <mruby.h>
+
+#ifndef MRB_NO_FLOAT
+
+#include <string.h>
+#include <math.h>
+
+MRB_API mrb_bool
+mrb_read_float(const char *str, char **endp, double *fp)
+{
+  const char *p = str;
+  const char *a = p;
+  double res = 0.0, frac = 0.0, div = 1.0;
+  int sign = 1;
+  int digits = 0;
+
+  // Skip whitespace
+  while (ISSPACE((unsigned char)*p)) p++;
+
+  // Handle sign
+  if (*p == '-') { sign = -1; p++; }
+  else if (*p == '+') p++;
+
+  // Parse integer part
+  while (ISDIGIT(*p)) {
+    res = res * 10.0 + (*p - '0');
+    digits++;
+    a = ++p;
+  }
+
+  // Parse fractional part
+  if (*p == '.') {
+    p++;
+    while (ISDIGIT(*p)) {
+      frac = frac * 10.0 + (*p++ - '0');
+      div *= 10.0;
+      digits++;
+    }
+    a = p;
+  }
+
+  // If no digits were found, return 0
+  if (digits == 0) {
+    if (endp) *endp = (char*)str;
+    *fp = 0.0;
+    return FALSE;
+  }
+
+  // Combine integer and fractional parts
+  res += frac / div;
+  res *= sign;
+
+  // Handle exponent
+  if ((*p | 32) == 'e') {
+    int e = 0;
+    sign = 1;
+    p++;
+    if (*p == '-') { sign = -1; p++; }
+    else if (*p == '+') p++;
+
+    // If no digits follow 'e', ignore the exponent part
+    if (!ISDIGIT(*p)) goto done;
+
+    while (ISDIGIT(*p)) {
+      if (e < 10000)  // 10000 is big enough to get Infinity
+        e = e * 10 + (*p - '0');
+      p++;
+    }
+    res *= pow(10.0, sign * e);
+    a = p;
+  }
+
+  // Set endp
+ done:
+  if (endp) *endp = (char*)a;
+  *fp = res;
+
+  // strtod(3) stores ERANGE to errno for overflow/underflow
+  // mruby does not require those checks
+#if 0
+  // Check for underflow after applying the exponent
+  if (res != 0.0 && fabs(res) < DBL_MIN) {
+    return FALSE;
+  }
+
+  // Check if the result is infinity or NaN
+  if (isinf(res) || isnan(res)) {
+    return FALSE;
+  }
+#endif
+  return TRUE;
+}
+
+#endif