diff liboctave/util/oct-string.cc @ 26111:3e44ed9d50b6

Move rational_approx to liboctave (patch #9084). * libinterp/corefcn/pr-output.cc, liboctave/util/oct-string.[cc/h]: Move "rational_approx" from "pr-output.cc" to "oct-string.cc".
author Markus Mützel <markus.muetzel@gmx.de>
date Sun, 18 Nov 2018 15:50:01 +0100
parents b543cf12c63f
children 37e3aa267374
line wrap: on
line diff
--- a/liboctave/util/oct-string.cc	Sun Nov 18 15:44:36 2018 +0100
+++ b/liboctave/util/oct-string.cc	Sun Nov 18 15:50:01 2018 +0100
@@ -28,10 +28,12 @@
 #include <algorithm>
 #include <cctype>
 #include <cstring>
-
+#include <iomanip>
 #include <string>
 
 #include "Array.h"
+#include "lo-ieee.h"
+#include "lo-mappers.h"
 
 template <typename T>
 static bool
@@ -481,3 +483,102 @@
 
   return val;
 }
+
+template <typename T>
+std::string
+rational_approx (T val, int len)
+{
+  std::string s;
+
+  if (len <= 0)
+    len = 10;
+
+  if (octave::math::isinf (val))
+    s = "1/0";
+  else if (octave::math::isnan (val))
+    s = "0/0";
+  else if (val < std::numeric_limits<int>::min ()
+           || val > std::numeric_limits<int>::max ()
+           || octave::math::x_nint (val) == val)
+    {
+      std::ostringstream buf;
+      buf.flags (std::ios::fixed);
+      buf << std::setprecision (0) << octave::math::round (val);
+      s = buf.str ();
+    }
+  else
+    {
+      T lastn = 1;
+      T lastd = 0;
+      T n = octave::math::round (val);
+      T d = 1;
+      T frac = val - n;
+      int m = 0;
+
+      std::ostringstream buf2;
+      buf2.flags (std::ios::fixed);
+      buf2 << std::setprecision (0) << static_cast<int> (n);
+      s = buf2.str ();
+
+      while (true)
+        {
+          T flip = 1 / frac;
+          T step = octave::math::round (flip);
+          T nextn = n;
+          T nextd = d;
+
+          // Have we converged to 1/intmax ?
+          if (std::abs (flip) > static_cast<T> (std::numeric_limits<int>::max ()))
+            {
+              lastn = n;
+              lastd = d;
+              break;
+            }
+
+          frac = flip - step;
+          n = step * n + lastn;
+          d = step * d + lastd;
+          lastn = nextn;
+          lastd = nextd;
+
+          std::ostringstream buf;
+          buf.flags (std::ios::fixed);
+          buf << std::setprecision (0) << static_cast<int> (n)
+              << '/' << static_cast<int> (d);
+          m++;
+
+          if (n < 0 && d < 0)
+            {
+              // Double negative, string can be two characters longer.
+              if (buf.str ().length () > static_cast<unsigned int> (len + 2))
+                break;
+            }
+          else if (buf.str ().length () > static_cast<unsigned int> (len))
+            break;
+
+          if (std::abs (n) > std::numeric_limits<int>::max ()
+              || std::abs (d) > std::numeric_limits<int>::max ())
+            break;
+
+          s = buf.str ();
+        }
+
+      if (lastd < 0)
+        {
+          // Move sign to the top
+          lastd = - lastd;
+          lastn = - lastn;
+          std::ostringstream buf;
+          buf.flags (std::ios::fixed);
+          buf << std::setprecision (0) << static_cast<int> (lastn)
+              << '/' << static_cast<int> (lastd);
+          s = buf.str ();
+        }
+    }
+
+  return s;
+}
+
+// instanciate the template for float and double
+template std::string rational_approx <float> (float val, int len);
+template std::string rational_approx <double> (double val, int len);