I'm doing a university project that requires using non-integers. We are coding in Assembly and the microprocessor we're using doesn't support floats. I'd like to know if there are any workarounds for this limitation. Mind you, I'm not looking for code (the microprocessor was designed specifically for learning purposes + it would be cheating), but I found the Wikipedia article about fixed-point arithmetics a bit vague. I got the impression that I need to represent the number as an integer, but I'm not sure if that means multiplying the number by a power of 2/10 or using the shift set of instructions (or even another solution). Any help would be appreciated.
1 Answers
Fixed point numbers are simply numbers which have been multiplied by a scaling factor. The scaling factor can be anything you choose, but of course it must remain constant throughout your calculation.
If you are doing financial calculations you might choose a scaling factor of 100, so that everything is calculated in cents, or you might even go for 1,000 so that you get to the tenth of a cent if you needed it to ward off rounding errors arising from interest calculations.
There is nothing to say that your scaling factor has to be a power of 10 - it could be a power of 2, or it could be any other number that makes sense in your application (the Wikipedia article gives the example of using 3,600 to calculate time intervals in hours using 1-second resolution).
As for your code - you just use regular integer arithmetic, and just remember that there is a scaling factor involved. The scaling factor only really matters during input and output. For example, if you are doing financial calculations with a scaling factor of 100, and the question is "what is one-fifth of $4.20?", you would take the input 4.20, multiply by your scaling factor to get 420, do an integer division by 5 to get 84, then divide by your scaling factor to get the answer of "$0.84".

- 31
- 1
-
It's nearly universal to use a power of 2 as the scaling factor, so we can use shifts instead of slow integer division! This is an assembly question... – Peter Cordes Oct 22 '18 at 04:11
-
It's not that difficult to multiply / divide by 100 in assembly when doing input/output and financial calculations. It also doesn't make sense to use powers of 2 when dealing with calculations involving time intervals. And even when you use powers of 2, you have to choose which power of 2, which depends on your application. And even then, conversion to/from powers of 10 (which is what most humans use) probably has to be done during input and output. So let the decision be driven by your application, and not by the available instruction set. – Deirdre O'Byrne Oct 22 '18 at 17:43
-
Everything's easy if you don't care about performance. On modern x86, multiply is only slightly slower than shifts (like 3 cycle latency vs. 1, and maybe only 1 per clock throughput), so dividing by 1000 (using a fixed-point multiplicative inverse: [Why does GCC use multiplication by a strange number in implementing integer division?](https://stackoverflow.com/q/41183935)) is maybe only ~4x worse latency than a right shift by 10 bits. But on older CPUs (especially without hardware FPUs), multiply is much slower (lower throughput and worse latency). – Peter Cordes Oct 22 '18 at 17:55
-
The default choice for computation performance (e.g. signal processing) is a power of 2; you'd only use something else in special cases like you mentioned: financial or if I/O was more common than multiplication. (Addition / subtraction don't need rescaling, and are equally efficient for any choice of scale factor.) – Peter Cordes Oct 22 '18 at 17:58
-
Multiplication and division don't need rescaling either. Indeed the whole point of fixed point is to make multiplication and division a whole lot easier. – Deirdre O'Byrne Oct 22 '18 at 18:03
-
Yes they do, if you're multiply two fixed-point numbers together. In fixed point, the integer value `x` represents `X = x/1024` (using 10 fraction bits and the rest as integer bits). If we want to compute `X * Y`, simply multiplying x and y would give us a number that represents `x/1024 * y/1024`, which has the fixed point in a different place than the inputs (20 fraction bits). To regain our original format, we need to right-shift by 10 to return the scale factor to 2^10, instead of 2^20. Sometimes you could just keep track of the current format, or multiply by unscaled integer constants. – Peter Cordes Oct 22 '18 at 18:23
-
https://en.wikipedia.org/wiki/Fixed-point_arithmetic#Binary_vs._decimal cites rescaling with fast shifts as the main reason binary fixed-point is more common. I also found https://www.allaboutcircuits.com/technical-articles/multiplication-examples-using-the-fixed-point-representation/, but it doesn't talk much about rescaling after a multiply. It does show how the point is in a different place, though. Still a useful link for future readers wanting to implement fixed-point in asm. – Peter Cordes Oct 22 '18 at 18:26
-
OK - I think we got crossed wires. For some reason I was thinking about applications like financial or time intervals - you never multiply one dollar amount by another, or one fraction of an hour by another. I think things escalated from there... – Deirdre O'Byrne Oct 22 '18 at 20:21
-
Yup, I was forgetting about those use-cases (where you might instead use decimal floating point, which some PowerPC chips support in hardware). I was only thinking about stuff like signal processing, or a Mandelbrot fractal drawing program on an old CPU like m68k, where the source I still have does `imag2 = (imag * imag) >> 13;` and so on. Binary fixed-point isn't universal, but I think it is more common. Also note for a financial use-case, you would still want to multiply by fractional values sometimes, like `1.01` to grow by 1%, or `.15` to calculate 15% tax. – Peter Cordes Oct 22 '18 at 20:27
-
Which all goes to show that it all depends utterly on your application! – Deirdre O'Byrne Oct 22 '18 at 23:34