1

I have an array that grows with each iteration of a loop:

for i in range(100):
    frac[i] = some fraction between 0 and 1 with many decimal places

This all works fine. When I check the type(frac[i]), I am told that it is 'numpy.float64'.

For my code to be as precise as I need it to be, I need to use the decimal module and change each frac[i] to the decimal type.

I updated my code:

for i in range(100):
    frac[i] = some fraction between 0 and 1 with many decimal places

    frac[i] = decimal.Decimal(frac[i])

But when I check the type, I am STILL told that frac[i] is 'numpy.float64'.

I have managed to change other variables to decimal in this way before, so I wonder if you could tell me why this doesn't seem to work.

Thank you.

Maxime Lorant
  • 34,607
  • 19
  • 87
  • 97
user1551817
  • 6,693
  • 22
  • 72
  • 109
  • 4
    Converting to a `Decimal` from any kind of float won't add precision; that precision is lost **forever**. – Ignacio Vazquez-Abrams Aug 31 '13 at 17:32
  • 1
    See a [previous question](http://stackoverflow.com/questions/7770870/numpy-array-with-dtype-decimal) on using decimal.Decimal with numpy arrays. – Paulo Almeida Aug 31 '13 at 17:45
  • I understand your point. I don't need to add precision when converting this float to decimal, I am just doing it so that all my variables are decimals, which I seem to need to do in order to get my equations to work. – user1551817 Aug 31 '13 at 17:46
  • You assign to a float array, so the value is cast to float. What else would you expect... – seberg Aug 31 '13 at 21:05

4 Answers4

1

Depending where your fractions are coming from, you may find it ideal to use the fractions module. Some examples from the docs:

>>> from fractions import Fraction
>>> Fraction(16, -10)
Fraction(-8, 5)
>>> Fraction(123)
Fraction(123, 1)
>>> Fraction()
Fraction(0, 1)
>>> Fraction('3/7')
Fraction(3, 7)
>>> Fraction(' -3/7 ')
Fraction(-3, 7)
>>> Fraction('1.414213 \t\n')
Fraction(1414213, 1000000)
>>> Fraction('-.125')
Fraction(-1, 8)
>>> Fraction('7e-6')
Fraction(7, 1000000)
>>> Fraction(2.25)
Fraction(9, 4)
>>> Fraction(1.1)
Fraction(2476979795053773, 2251799813685248)
>>> from decimal import Decimal
>>> Fraction(Decimal('1.1'))
Fraction(11, 10)

You can also perform all of the regular arithmetic operations; if the result can't be expressed as a fraction, it will be converted to a float:

>>> Fraction(3, 4) + Fraction(1, 16)
Fraction(13, 16)
>>> Fraction(3, 4) * Fraction(1, 16)
Fraction(3, 64)
>>> Fraction(3, 4) ** Fraction(1, 16)
0.982180548555
intuited
  • 23,174
  • 7
  • 66
  • 88
  • Thank you. But the number I am using in my example is just a random number really. I just need to convert it to decimal, because I have other decimals in my code (they need to be decimals) and unless I have all variables of the same type, my equations don't seem to work. Hence, I just want to convert these floats to decimals. – user1551817 Aug 31 '13 at 17:52
1

Note: I haven't used numpy at all, so what follows is mostly just an educated guess.

It sounds like you are using a typed array of type float64. Typed arrays are a particular feature of numpy — the elements of arrays (actually Lists) in Python itself can change dynamically from type to type, and there is no need for all elements of a Python list to have the same type.

With a float64-type array, your values are being cast to floats as they are assigned to array elements, undoing whatever type-casting you've done to them before that point.

The documentation for numpy array creation mentions that the default array type is float64. You probably need to change this to Decimal.

Adding the keyword argument dtype=Decimal to a call to np.arange should do this. You should then have an array of type Decimal, and any float or float64 values you assign it should be cast to Decimal. I don't know enough about what you're doing, or about numpy, to know if this is a sensible thing to be doing with a numpy array.

intuited
  • 23,174
  • 7
  • 66
  • 88
1

I was just playing around with Decimals with Numpy.

I found that Numpy offers a function called np.vectorize that allows you to take a function and apply it over a numpy array. In [23]:

import numpy as np

import decimal

D = decimal.Decimal

We'll create a regular np float array

In [24]:

f10 = np.random.ranf(10)

f10

Out[24]:

array([ 0.45410583,  0.35353919,  0.5976785 ,  0.12030978,  0.00976334,
        0.47035594,  0.76010096,  0.09229687,  0.24842551,  0.30564141])

trying to convert the array using np.asarray to Decimal type doesn't work. It seems that trying to use np.asarray and specifying the decimal.Decimal type sets the array to object which is to be expected but if you actually access an individual element of the array it still has a float data type. In [25]:

f10todec = np.asarray(f10, dtype = decimal.Decimal)

print f10todec.dtype, f10todec

print type(f10todec[0])

object [0.454105831376884 0.3535391906233327 0.5976785016396975 0.1203097778312584
 0.009763339031407026 0.47035593879363524 0.7601009625324361
 0.09229687387940333 0.24842550566826282 0.30564141425653435]
<type 'float'>

If you give np.array a homogenous python list of Decimal types then it seems to preserve the type, hense the list comprehension below to get a list of the values in the first array as Decimal datatype. So I had to make the decimal array this way.

In [26]:

D10 = np.array([D(d) for d in f10])

D10

Out[26]:

array([Decimal('0.4541058313768839838076019077561795711517333984375'),
       Decimal('0.35353919062333272194109667907468974590301513671875'),
       Decimal('0.597678501639697490332991947070695459842681884765625'),
       Decimal('0.12030977783125840208100498784915544092655181884765625'),
       Decimal('0.00976333903140702563661079693702049553394317626953125'),
       Decimal('0.47035593879363524205672320022131316363811492919921875'),
       Decimal('0.76010096253243608632743644193396903574466705322265625'),
       Decimal('0.09229687387940332943259136300184763967990875244140625'),
       Decimal('0.24842550566826282487653543284977786242961883544921875'),
       Decimal('0.30564141425653434946951847450691275298595428466796875')], dtype=object)

basic math operations seem to work ok

In [27]:

D10/2

Out[27]:

array([Decimal('0.2270529156884419919038009539'),
       Decimal('0.1767695953116663609705483395'),
       Decimal('0.2988392508198487451664959735'),
       Decimal('0.06015488891562920104050249392'),
       Decimal('0.004881669515703512818305398469'),
       Decimal('0.2351779693968176210283616001'),
       Decimal('0.3800504812662180431637182210'),
       Decimal('0.04614843693970166471629568150'),
       Decimal('0.1242127528341314124382677164'),
       Decimal('0.1528207071282671747347592373')], dtype=object)

In [28]:

np.sqrt(D10)

Out[28]:

array([Decimal('0.6738737503248542354573624759'),
       Decimal('0.5945916166776426405934196108'),
       Decimal('0.7730966961769384578392278689'),
       Decimal('0.3468569991095154505863255680'),
       Decimal('0.09880961001545864636229121433'),
       Decimal('0.6858250059553349663476168402'),
       Decimal('0.8718376927688066448819998853'),
       Decimal('0.3038040057000620415496242404'),
       Decimal('0.4984230187985531079935481296'),
       Decimal('0.5528484550548498633920483390')], dtype=object)

Untill you try a trig function for which there is no corresponding function in the decimal module In [29]:

np.sin(D10)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)

<ipython-input-29-31ba62da35b8> in <module>()
----> 1 np.sin(D10)

AttributeError: 'Decimal' object has no attribute 'sin'

so lets use np.vectorize so we can use decimal's quantize function to do rounding.

In [30]:

    npquantize = np.vectorize(decimal.Decimal.quantize)

    qnt_D10 = npquantize(D10, D('.000001'))

    qnt_D10

Out[30]:

    array([Decimal('0.454106'), Decimal('0.353539'), Decimal('0.597679'),
           Decimal('0.120310'), Decimal('0.009763'), Decimal('0.470356'),
           Decimal('0.760101'), Decimal('0.092297'), Decimal('0.248426'),
           Decimal('0.305641')], dtype=object)

You also need to be careful about some regular python math functions because they will automaticaly change the return type to float. I assume this is because the number can't be calculated accuratly based on the function like SIN or COS.

so i guess the short answer is use a list comprehension to get and convert the items in a numpy array to a python list then create that array from the list of Decimals.

To return numpy arrays with their type intact I guess you could use the vectorize function to wrap any function that works with Decimal type to apply over the np array.

On a side note there is a module in the pip that provides numpy style arrays with IEEE Decimals https://pypi.python.org/pypi/decimalpy/0.1

Drafter250
  • 138
  • 9
0

Try doing decimal.Decimal.from_float(frac[i])

Akshay
  • 783
  • 6
  • 20