Behdad Esfahbod | 2d67a18 | 2013-11-28 18:08:36 -0500 | [diff] [blame] | 1 | """fontTools.misc.fixedTools.py -- tools for working with fixed numbers. |
| 2 | """ |
| 3 | |
Behdad Esfahbod | 1ae2959 | 2014-01-14 15:07:50 +0800 | [diff] [blame^] | 4 | from __future__ import print_function, division, absolute_import |
Behdad Esfahbod | 2d67a18 | 2013-11-28 18:08:36 -0500 | [diff] [blame] | 5 | from fontTools.misc.py23 import * |
| 6 | |
| 7 | __all__ = [ |
| 8 | "fixedToFloat", |
| 9 | "floatToFixed", |
| 10 | ] |
| 11 | |
| 12 | def fixedToFloat(value, precisionBits): |
| 13 | """Converts a fixed-point number to a float, choosing the float |
| 14 | that has the shortest decimal reprentation. Eg. to convert a |
| 15 | fixed number in a 2.14 format, use precisionBits=14. This is |
| 16 | pretty slow compared to a simple division. Use sporadically. |
| 17 | |
| 18 | >>> fixedToFloat(13107, 14) |
| 19 | 0.8 |
| 20 | >>> fixedToFloat(0, 14) |
| 21 | 0.0 |
| 22 | >>> fixedToFloat(0x4000, 14) |
| 23 | 1.0 |
| 24 | """ |
| 25 | |
| 26 | if not value: return 0.0 |
| 27 | |
| 28 | scale = 1 << precisionBits |
| 29 | value /= scale |
| 30 | eps = .5 / scale |
| 31 | digits = (precisionBits + 2) // 3 |
| 32 | fmt = "%%.%df" % digits |
| 33 | lo = fmt % (value - eps) |
| 34 | hi = fmt % (value + eps) |
| 35 | out = [] |
| 36 | length = min(len(lo), len(hi)) |
| 37 | for i in range(length): |
| 38 | if lo[i] != hi[i]: |
| 39 | break; |
| 40 | out.append(lo[i]) |
| 41 | outlen = len(out) |
| 42 | if outlen < length: |
| 43 | out.append(max(lo[outlen], hi[outlen])) |
| 44 | return float(strjoin(out)) |
| 45 | |
| 46 | def floatToFixed(value, precisionBits): |
| 47 | """Converts a float to a fixed-point number given the number of |
| 48 | precisionBits. Ie. int(round(value * (1<<precisionBits))). |
| 49 | |
| 50 | >>> floatToFixed(0.8, 14) |
| 51 | 13107 |
| 52 | >>> floatToFixed(1.0, 14) |
| 53 | 16384 |
| 54 | >>> floatToFixed(1, 14) |
| 55 | 16384 |
| 56 | >>> floatToFixed(0, 14) |
| 57 | 0 |
| 58 | """ |
| 59 | |
| 60 | return int(round(value * (1<<precisionBits))) |
| 61 | |
| 62 | |
| 63 | if __name__ == "__main__": |
| 64 | import doctest |
| 65 | doctest.testmod() |