"""fontTools.misc.fixedTools.py -- tools for working with fixed numbers. """ from __future__ import print_function, division, absolute_import from fontTools.misc.py23 import * __all__ = [ "fixedToFloat", "floatToFixed", ] def fixedToFloat(value, precisionBits): """Converts a fixed-point number to a float, choosing the float that has the shortest decimal reprentation. Eg. to convert a fixed number in a 2.14 format, use precisionBits=14. This is pretty slow compared to a simple division. Use sporadically. >>> "%g" % fixedToFloat(13107, 14) '0.8' >>> "%g" % fixedToFloat(0, 14) '0' >>> "%g" % fixedToFloat(0x4000, 14) '1' >>> "%g" % fixedToFloat(-16384, 14) '-1' >>> "%g" % fixedToFloat(-16383, 14) '-0.99994' >>> "%g" % fixedToFloat(16384, 14) '1' >>> "%g" % fixedToFloat(16383, 14) '0.99994' >>> "%g" % fixedToFloat(-639, 6) '-9.99' >>> "%g" % fixedToFloat(-640, 6) '-10' >>> "%g" % fixedToFloat(639, 6) '9.99' >>> "%g" % fixedToFloat(640, 6) '10' """ if not value: return 0.0 scale = 1 << precisionBits value /= scale eps = .5 / scale lo = value - eps hi = value + eps # If the range of valid choices spans an integer, return the integer. if int(lo) != int(hi): return round(value) fmt = "%%.%df" % ((precisionBits + 2) // 3) lo = fmt % lo hi = fmt % hi i = 0 length = min(len(lo), len(hi)) while i < length and lo[i] == hi[i]: i += 1 out = lo[:i] assert -1 != out.find('.') # Both ends should be the same past decimal point if i < length: # Append mid-point digit of half-open range (l,h]. out = out + str((int(lo[i]) + int(hi[i]) + 1) // 2) return float(out) def floatToFixed(value, precisionBits): """Converts a float to a fixed-point number given the number of precisionBits. Ie. int(round(value * (1<>> floatToFixed(0.8, 14) 13107 >>> floatToFixed(1.0, 14) 16384 >>> floatToFixed(1, 14) 16384 >>> floatToFixed(-1.0, 14) -16384 >>> floatToFixed(-1, 14) -16384 >>> floatToFixed(0, 14) 0 """ return int(round(value * (1<