Python half precision floating point

Posted by davidejones

I’ve been working with python for a while to create some addons for Blender which allowing me to import and export various formats that aren’t supported by default. One of these file formats I’ve been working with compresses the vertices/uv/tangent/normal data to reduce the space used. This data is usually stored as 32bit floating point values but in this case it is reduced to 16bit floating point or Half Precision floating point. Halfing the final size of this data.

I had a hard time recreating this with python, I’d never used python before starting using Blender so my understanding was still fairly new and most examples online I failed to understand or struggled to implement.

Finally I cobbled together something that works from different examples online and got the results I wanted. Like most things on my site I felt the need to share this with anyone else who may be trying a similar thing.

Here is my class in python

class Float16Compressor:
	def __init__(self):
		self.temp = 0
		
	def compress(self,float32):
		F16_EXPONENT_BITS = 0x1F
		F16_EXPONENT_SHIFT = 10
		F16_EXPONENT_BIAS = 15
		F16_MANTISSA_BITS = 0x3ff
		F16_MANTISSA_SHIFT =  (23 - F16_EXPONENT_SHIFT)
		F16_MAX_EXPONENT =  (F16_EXPONENT_BITS << F16_EXPONENT_SHIFT)

		a = struct.pack('>f',float32)
		b = binascii.hexlify(a)

		f32 = int(b,16)
		f16 = 0
		sign = (f32 >> 16) & 0x8000
		exponent = ((f32 >> 23) & 0xff) - 127
		mantissa = f32 & 0x007fffff
				
		if exponent == 128:
			f16 = sign | F16_MAX_EXPONENT
			if mantissa:
				f16 |= (mantissa & F16_MANTISSA_BITS)
		elif exponent > 15:
			f16 = sign | F16_MAX_EXPONENT
		elif exponent > -15:
			exponent += F16_EXPONENT_BIAS
			mantissa >>= F16_MANTISSA_SHIFT
			f16 = sign | exponent << F16_EXPONENT_SHIFT | mantissa
		else:
			f16 = sign
		return f16
		
	def decompress(self,float16):
		s = int((float16 >> 15) & 0x00000001)    # sign
		e = int((float16 >> 10) & 0x0000001f)    # exponent
		f = int(float16 & 0x000003ff)            # fraction

		if e == 0:
			if f == 0:
				return int(s << 31)
			else:
				while not (f & 0x00000400):
					f = f << 1
					e -= 1
				e += 1
				f &= ~0x00000400
				#print(s,e,f)
		elif e == 31:
			if f == 0:
				return int((s << 31) | 0x7f800000)
			else:
				return int((s << 31) | 0x7f800000 | (f << 13))

		e = e + (127 -15)
		f = f << 13
		return int((s << 31) | (e << 23) | f)

and here is how to use it

#read half float from file and print float
h = struct.unpack(">H",file.read(struct.calcsize(">H")))[0]
fcomp = Float16Compressor()
temp = fcomp.decompress(h)
str = struct.pack('I',temp)
f = struct.unpack('f',str)[0]
print(f)

#write half float to file from float
fcomp = Float16Compressor()
f16 = fcomp.compress(float32)
file.write(struct.pack(">H",f16))

Trackback URL for this post: http://davidejones.com/blog/1413-python-precision-floating-point/trackback/

Being Sociable...

  • If you like this article then please share it on your favourite social network and follow me on twitter for the latest updates

2 Responses to Python half precision floating point

AM

I would like to import some 32 bit floating point images I have (raw binary data, nor special formats) into blender and then texture map my object with them. Do you know if Blender supports this? Will it “damage” (quantize to 8-bit or 16 bit resolution, or truncate) my textures? Finally I would really need to export the texture coordinates developed with blender to another processing software I have. if you have any advice, sample code, or any comment, I would really appreciate it.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>