Python's standard float type is a C double: http://docs.python.org/2/library/stdtypes.html#typesnumeric
NumPy's standard numpy.float is the same, and is also the same as numpy.float64.
Python's standard float type is a C double: http://docs.python.org/2/library/stdtypes.html#typesnumeric
NumPy's standard numpy.float is the same, and is also the same as numpy.float64.
Data type-wise numpy floats and built-in Python floats are the same, however boolean operations on numpy floats return np.bool_ objects, which always return False for val is True. Example below:
In [1]: import numpy as np
...: an_np_float = np.float32(0.3)
...: a_normal_float = 0.3
...: print(a_normal_float, an_np_float)
...: print(type(a_normal_float), type(an_np_float))
0.3 0.3
<class 'float'> <class 'numpy.float32'>
Numpy floats can arise from scalar output of array operations. If you weren't checking the data type, it is easy to confuse numpy floats for native floats.
In [2]: criterion_fn = lambda x: x <= 0.5
...: criterion_fn(a_normal_float), criterion_fn(an_np_float)
Out[2]: (True, True)
Even boolean operations look correct. However the result of the numpy float isn't a native boolean datatype, and thus can't be truthy.
In [3]: criterion_fn(a_normal_float) is True, criterion_fn(an_np_float) is True
Out[3]: (True, False)
In [4]: type(criterion_fn(a_normal_float)), type(criterion_fn(an_np_float))
Out[4]: (bool, numpy.bool_)
According to this github thread, criterion_fn(an_np_float) == True will evaluate properly, but that goes against the PEP8 style guide.
Instead, extract the native float from the result of numpy operations. You can do an_np_float.item() to do it explicitly (ref: this SO post) or simply pass values through float().
Will numpy.float32 help?
>>>PI=3.1415926535897
>>> print PI*PI
9.86960440109
>>> PI32=numpy.float32(PI)
>>> print PI32*PI32
9.86961
If you want to do math operation on float32, convert the operands to float32 may help you.
Use numpy.ndarray.astype:
import numpy as np
arr_f64 = np.array([1.0000123456789, 2.0000123456789, 3.0000123456789], dtype=np.float64)
arr_f32 = arr_f64.astype(np.float32)
Pay attention to precision:
np.set_printoptions(precision=16)
print("arr_f64 = ", arr_f64)
print("arr_f32 = ", arr_f32)
gives
arr_f64 = [1.0000123456789 2.0000123456789 3.0000123456789]
arr_f32 = [1.0000124000000 2.0000124000000 3.0000124000000]
The numbers compare equal because 58682.7578125 can be exactly represented in both 32 and 64 bit floating point. Let's take a close look at the binary representation:
32 bit: 01000111011001010011101011000010
sign : 0
exponent: 10001110
fraction: 11001010011101011000010
64 bit: 0100000011101100101001110101100001000000000000000000000000000000
sign : 0
exponent: 10000001110
fraction: 1100101001110101100001000000000000000000000000000000
They have the same sign, the same exponent, and the same fraction - the extra bits in the 64 bit representation are filled with zeros.
No matter which way they are cast, they will compare equal. If you try a different number such as 58682.7578124 you will see that the representations differ at the binary level; 32 bit looses more precision and they won't compare equal.
(It's also easy to see in the binary representation that a float32 can be upcast to a float64 without any loss of information. That is what numpy is supposed to do before comparing both.)
import numpy as np
a = 58682.7578125
f32 = np.float32(a)
f64 = np.float64(a)
u32 = np.array(a, dtype=np.float32).view(dtype=np.uint32)
u64 = np.array(a, dtype=np.float64).view(dtype=np.uint64)
b32 = bin(u32)[2:]
b32 = '0' * (32-len(b32)) + b32 # add leading 0s
print('32 bit: ', b32)
print('sign : ', b32[0])
print('exponent: ', b32[1:9])
print('fraction: ', b32[9:])
print()
b64 = bin(u64)[2:]
b64 = '0' * (64-len(b64)) + b64 # add leading 0s
print('64 bit: ', b64)
print('sign : ', b64[0])
print('exponent: ', b64[1:12])
print('fraction: ', b64[12:])
The same value is stored internally, only it doesn't show all digits with a print
Try:
print "%0.8f" % float_32
See related Printing numpy.float64 with full precision
If you have the raw bytes (e.g. read from memory, from file, over the network, ...) you can use struct for this:
>>> import struct
>>> struct.unpack('>f', '\x3f\x9a\xec\xb5')[0]
1.2103487253189087
Here, \x3f\x9a\xec\xb5 are your input registers, 16282 (hex 0x3f9a) and 60597 (hex 0xecb5) expressed as bytes in a string. The > is the byte order mark.
So depending how you get the register values, you may be able to use this method (e.g. by converting your input integers to byte strings). You can use struct for this, too; this is your second example:
>>> raw = struct.pack('>HH', 16282, 1147) # from two unsigned shorts
>>> struct.unpack('>f', raw)[0] # to one float
1.2032617330551147
The way you've converting the two ints makes implicit assumptions about endianness that I believe are wrong.
So, let's back up a step. You know that the first argument is the most significant word, and the second is the least significant word. So, rather than try to figure out how to combine them into a hex string in the appropriate way, let's just do this:
import struct
import sys
first = sys.argv[1]
second = sys.argv[2]
sample = int(first) << 16 | int(second)
Now we can just convert like this:
def convert(i):
s = struct.pack('=i', i)
return struct.unpack('=f', s)[0]
And if I try it on your inputs:
$ python floatify.py 16282 60597
1.21034872532
$ python floatify.py 16282 1147
1.20326173306
Floating point values are inherently non-exact on computers. The python default float is a what's called a double precision floating point number on most machines according to https://docs.python.org/2/tutorial/floatingpoint.html. numpy.float32 is a single precision float. It's double precision counterpart is numpy.float64. This could explain the difference in this case.
In general floating point numbers shouldn't be compared directly using ==. You can use numpy.isclose to deal with the small errors caused by non-exact floating point representations.
Python float is a C double type: documentation:
Floating point numbers are usually implemented using double in C; information about the precision and internal representation of floating point numbers for the machine on which your program is running is available in
sys.float_info.
Therefore, you are comparing 32 and 64 precision floating point numbers. The following will work:
t = numpy.float64(.3)
x = numpy.float64(1)
r = numpy.float64(-.3)
_t = t+x+r
_t == 1
That's just because Python shows you the nicest representation of that number. The value actually is exactly this:
>>> '%.60f' % fl
'0.100000000000000005551115123125782702118158340454101562500000'
Which is exactly what the literal 0.1 turns into:
>>> '%.60f' % 0.1
'0.100000000000000005551115123125782702118158340454101562500000'
(Oh and it's not 0.10000000149011612 because that's done with 32 instead of 64 bits. And actually that one should be 0.100000001490116119384765625, that converter page is inaccurate.)
You can use the gmpy2 library to work with arbitrary precision binary numbers, including the standard 32-bit and 64-bit IEEE formats.
Here is an example:
>>> import gmpy2
>>> gmpy2.set_context(gmpy2.ieee(64))
>>> gmpy2.mpfr("0.1").__format__(".60f")
'0.100000000000000005551115123125782702118158340454101562500000'
>>> gmpy2.set_context(gmpy2.ieee(32))
>>> gmpy2.mpfr("0.1").__format__(".60f")
'0.100000001490116119384765625000000000000000000000000000000000'
Edit: Added example function to convert mpfr to 32-bit IEEE format.
import gmpy2
gmpy2.set_context(gmpy2.ieee(32))
def mpfr_to_float(x):
'''Convert an mpfr object created by the IEEE 32-bit compatible
context to a 32 character string containing 0 and 1.'''
# Check for special values first.
if gmpy2.is_infinite(x):
if gmpy2.is_signed(x):
return "1" * 9 + "0" * 23
else:
return "0" + "1" * 8 + "0" * 23
if gmpy2.is_nan(x):
return "0" + "1" * 31
if gmpy2.is_zero(x):
if gmpy2.is_signed(x):
return "1" + "0" * 31
else:
return "0" * 32
# Extract the mantissa, exponent, and precision. Note that the
# values are slightly different than the IEEE 32-bit standard.
mnt, exp, prc = x.digits(2)
# MPFR explicitely stores the leading bit of the mantissa so the
# precision is 24. To support subnormals, MPFR also uses a more
# negative minimum exponent and decreases the precision of the
# mantissa but maintains the leading '1' bit.
# Remove any leading sign bit from the mantissa string.
if mnt[0] == "-":
sign_char = "1"
mnt = mnt[1:]
else:
sign_char = "0"
# Check for subnormals
if exp + 126 <= 0:
# Drop the last bit since it will always be '0' and after the
# adjustments for subnormals, the leading bit will be '0'.
mnt = mnt[:-1]
else:
# Drop the leading '1' bit for normal numbers.
mnt = mnt[1:]
# Handle subnormals by shifting trailing bits from the mantissa
# string to the beginning. Adjust the exponent to match.
while exp + 126 < 0:
mnt = mnt[-1] + mnt[:-1]
exp = exp + 1
# Adjust the exponent to account for removing a bit from the
# mantissa string.
exp = exp - 1
# Add 127 to the exponent to account for the IEEE encoding.
exp = exp + 127
# Validate exponent range.
if (exp > 255) or (exp < 0):
raise ValueError("exp is out of bounds")
# Build and return the binary string.
result = sign_char + format(exp, "08b") + mnt
if len(result) != 32:
raise ValueError("something is wrong....")
return result
if __name__ == "__main__":
print(mpfr_to_float(gmpy2.mpfr("0.1")))
Disclaimer #1: This should really be a comment to @Stefan Pochmann's answer but I thought a code example would be helpful.
Disclaimer #2: I maintain gmpy2.