Repository: leonardt/hwtypes

Issue:
FPVector is missing "to_fp" method
This would cast a BitVector to floating point.

```
BFloat16 = FPVector[8,7,...]
assert BFloat16.to_fp(BitVector[16](10)) == BFloat16(10.0)
```

Discussion:

Patch:
diff --git a/README.md b/README.md
index 1324025..2d1e3ed 100644
--- a/README.md
+++ b/README.md
@@ -12,9 +12,3 @@ pip install hwtypes
 brew install gmp mpfr libmpc
 pip install hwtypes
 ```
-
-## CentOS
-```
-yum install libmpc-devel mpfr-devel gmp-devel
-pip install hwtypes
-```
diff --git a/hwtypes/fp_vector.py b/hwtypes/fp_vector.py
index 68d833c..9fd7320 100644
--- a/hwtypes/fp_vector.py
+++ b/hwtypes/fp_vector.py
@@ -1,11 +1,11 @@
-import typing as tp
 import functools
-import random
 import gmpy2
+import random
+import typing as tp
 import warnings
 
-from .fp_vector_abc import AbstractFPVector, RoundingMode
 from .bit_vector import Bit, BitVector, SIntVector
+from .fp_vector_abc import AbstractFPVector, RoundingMode
 
 __ALL__ = ['FPVector']
 
@@ -42,7 +42,36 @@ def set_context(fn: tp.Callable) -> tp.Callable:
 class FPVector(AbstractFPVector):
     @set_context
     def __init__(self, value):
-        value = gmpy2.mpfr(value)
+        # Because for some reason gmpy2.mpfr is a function and not a type
+        if isinstance(value, type(gmpy2.mpfr(0))):
+            #need to specify precision because mpfr will use the input
+            #precision not the context precision when constructing from mpfr
+            value = gmpy2.mpfr(value, self._ctx_.precision)
+        elif isinstance(value, FPVector):
+            value = gmpy2.mpfr(value._value, self._ctx_.precision)
+        elif isinstance(value, (int, float, type(gmpy2.mpz(0)), type(gmpy2.mpq(0)))):
+            value = gmpy2.mpfr(value)
+        elif isinstance(value, str):
+            try:
+                #Handles '0.5'
+                value = gmpy2.mpfr(value)
+            except ValueError:
+                try:
+                    #Handles '1/2'
+                    value = gmpy2.mpfr(gmpy2.mpq(value))
+                except ValueError:
+                    raise ValueError('Invalid string')
+        elif hasattr(value, '__float__'):
+            value = gmpy2.mpfr(float(value))
+        elif hasattr(value, '__int__'):
+            value = gmpy2.mpfr(int(value))
+        else:
+            try:
+                #if gmpy2 doesn't complain I wont
+                value = gmpy2.mpfr(value)
+            except TypeError:
+                raise TypeError(f"Can't construct FPVector from {type(value)}")
+
         if gmpy2.is_nan(value) and not type(self).ieee_compliance:
             if gmpy2.is_signed(value):
                 self._value = gmpy2.mpfr('-inf')


Test patch:
diff --git a/tests/test_fp.py b/tests/test_fp.py
index 7cff76d..20628da 100644
--- a/tests/test_fp.py
+++ b/tests/test_fp.py
@@ -127,6 +127,30 @@ def _c_type_vector(T):
 
 NTESTS = 100
 
+@pytest.mark.parametrize("mode", [
+    RoundingMode.RNE,
+    RoundingMode.RNA,
+    RoundingMode.RTP,
+    RoundingMode.RTN,
+    RoundingMode.RTZ,
+    ])
+@pytest.mark.parametrize("ieee", [False, True])
+def test_init(mode, ieee):
+    BigFloat = FPVector[27,100, mode, ieee]
+    class F:
+        def __float__(self):
+            return 0.5
+
+    class I:
+        def __int__(self):
+            return 1
+
+    assert BigFloat(0.5) == BigFloat(F())
+    assert BigFloat(1) == BigFloat(I())
+    assert BigFloat(0.5) == BigFloat('0.5')
+    assert BigFloat('1/3') == BigFloat(1)/BigFloat(3)
+    assert BigFloat('1/3') != BigFloat(1/3) # as 1/3 is performed in python floats
+
 @pytest.mark.parametrize("FT", [
     FPVector[8, 7, RoundingMode.RNE, True],
     FPVector[8, 7, RoundingMode.RNE, False],