Calculating with real numbers without FPU - Page 1
Forum section: electronics / general

author: nForce
author_rank: Frequent Contributor
timestamp: January 23, 2019, 03:36:35 pm
content: If we have a microcontroller which does not have an FPU. How would we calculate:1.713 * 0.05141.713 + 0.0514Thanks.

author: helius
author_rank: Super Contributor
timestamp: January 23, 2019, 03:47:44 pm
content: If your numbers are all in about the same range, fixed point is easiest. That is basically the same as computation on integers, where you choose a small power of 2 to be your unit.For example, 1.713 can be represented as a fixed-point with 12 fraction bits:Code:1 * (1<<12) + 2920 == 1.713 * (1<<12)Then addition and subtraction works as normal, and multiplication or division also requires a 12 bit shift.

author: rhodges
author_rank: Frequent Contributor
timestamp: January 23, 2019, 03:52:42 pm
content: Usually, I can get a "good enough" result by multiplying the floating point number by 2^N. Then use that integer and shift or drop N bits afterwards.1.713 * 2^16 = 1122630.0514 * 2^17 = 6737112263 * 6737 = 756315831756315831 >> 33 = 0 (less than 1)If you want to use the fractional part of the result, don't shift or drop all N bits.

author: nForce
author_rank: Frequent Contributor
timestamp: January 23, 2019, 04:11:56 pm
content: Hmm, ok but what about for example this fixed point format:https://en.wikipedia.org/wiki/Q_(number_format) ?

author: NorthGuy
author_rank: Super Contributor
timestamp: January 23, 2019, 04:12:43 pm
content: You can do floating point calculations without FPU. Usually, the C compiler will have built-in software libraries to do this. So, you won't even notice the difference (except for performance, of course).Whether you should use floating point is a different questions. In most cases, you can use scaled integers instead. Thus you wouldn't calculate floating point things on a small MCU in the first place.

author: rhodges
author_rank: Frequent Contributor
timestamp: January 23, 2019, 04:49:33 pm
content: @nForce: Thanks for the link. It is better than my example.Here is an example to convert liters to gallons and get the ASCII character for the tenths gallons. It is "close enough" for my needs.Code:/******************************************************************************** Convert liters to gallons** in: liters* out: gallons * 32 (low 5 bits for decimal point)*/int conv_gallons(int liters){unsigned long temp;temp = (unsigned int)liters;temp *= 541;temp >>= 6;return temp;}/******************************************************************************** Get decimal point char from bottom 5 bits** in: 5 bits of fractional number* out: decimal character*/char conv_point(char frac){return ((frac * 10) >> 5) + '0';}

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 23, 2019, 05:14:29 pm
content: As NorthGuy said, you can do floating point on pretty much most MCUs. Those without an FPU will have terrible performance with FP, but that may be adequate for your needs.If performance is an issue, use integers instead. Either through fixed point arithmetic, or just by rewriting your calculations with scaled integers (both are close).Yet another, less common approach is to express the numbers as rationals N/M which will avoid approximations as long as all your numbers are rational to begin with (but it will usually require the use of "large integers"). Has its uses, but it's not a general-purpose solution so you'd need to make sure it's relevant for what you need to calculate. If you're just considering using floating point numbers (but won't for performance reasons), it's probably not the approach for you.

author: legacy
author_rank: Super Contributor
timestamp: January 23, 2019, 05:24:21 pm
content: softfloat

author: nForce
author_rank: Frequent Contributor
timestamp: January 23, 2019, 05:55:47 pm
content: Ok, let's forget that we have an FPU. What about if we use old microcontrollers.And rhodges this a great example:QuoteWhat if we have only a 4-digit display? Now what? Now we can't display 112263.

author: nctnico
author_rank: Super Contributor
timestamp: January 23, 2019, 05:59:09 pm
content: Quote from: nForce on January 23, 2019, 03:36:35 pmBy using soft floating point. It comes standard with every C library. There is one downside: you will need more flash and the speed will be slower. If your application can tolerate that, then you are fine and I wouldn't bother with using fixed point math. If you use an ARM microcontroller it is likely fast enough to use floating point in realtime scenarios as well. I'd try first to see if you get the required performance using soft floating point and only optimise if the performance isn't good enough.

author: tsman
author_rank: Frequent Contributor
timestamp: January 23, 2019, 06:21:03 pm
content: Quote from: nForce on January 23, 2019, 05:55:47 pmThat is a really vague question. You as the designer need to decide what is appropriate as a workaround as this is specific to your device. If those digits are essential then you'll need to redesign your device to have a bigger display. If you don't care then truncate the extra digits when displaying them.

author: ejeffrey
author_rank: Super Contributor
timestamp: January 23, 2019, 06:24:05 pm
content: I frequently run into cases where I am only doing a small amount of floating point computation. Perhaps just formatting for display, or applying a rescaling to a low speed ADC such as a power supply monitor. In that case I use soft float if it will fit in the available flash memory. It is easy as flipping a switch, completely obvious what you are doing, and you don't have to worry about manually scaling multiplication. It also lets you use the rest of the standard math library, such as trig and exponent functions which might be useful -- for instance converting linear amplitude to dB or computing sqrt(a^2+b^2). There are fixed point algorithms for all of those, but if the provided math libraries work, just use them.For even slightly performance critical cases, fixed point is the way to go. If you need the dynamic range of floating point and you care at all about performance you need a microcontroller with an FPU.There as some applications where fixed point is actually desirable or needed. For instance, it is usually desirable to implement FIR filters in fixed point. A FIR filter has unity gain if the coefficients sum to 1. With floating point if the exponent changes you can get (small) signal dependent gain variation which might be a problem for you. A high order FIR boxcar averager can be implemented efficiently by adding the current item and subtracting the Nth previous item. This reduces N operations per cycle to 2 (at a cost of N memory). But this doesn't work well with floating point as the subtraction won't exactly cancel the addition if the floating point exponent changes.Obviously correctness is more important than convenience. If you application requires fixed point use that.

author: 0xdeadbeef
author_rank: Super Contributor
timestamp: January 23, 2019, 06:48:05 pm
content: Fixed point integer format is typically way to go since software float is slow as hell. So with any kind of real time requirement, software float is usually not an option.Also keep in mind that floating point doesn't magically increase the precision. For 32bit floating point, there are still only 1<<32 different values but the range if fixed. So in a smaller range, a cleverly implemented fixed point calculation will be more precise than a float implementation of the same width (e.g. 32bit). Many people aren't even aware that any integer greater than 1<<24 can't be exactly represented in 32bit float. E.g. the integer 16777216 can be represented precisely in 32bit float, but the integer 16777217 can't. And of course the inaccuracy increases for larger (absolute) values. E.g. towards the end of the unsigned 32bit integer range, there are no integer values between 4294967040 and 4294967296 than can be represented. That's a gap of 254 integer numbers that simply can't be represented.What people also tend to forget is that float calculations are not as idiot proof as integer calculations. E.g. while most CPUs saturate integer calculations like n/0 to return the largest possible integer with correct sign, this is not necessarily the case for float. Actually n/0 is +/-infinity in IEEE float (actual implementation might differ). Also you can easily end up with denormalized ("subnormal") numbers that still have a (very small) value but can't be used for further calculations. Finally, 0/0 officially results in "not a number" where there can be "signaling" and "quiet" NaNs. In many cases, you either have to check your values for INF, NaN or subnormals or have to implement exceptions handlers which handle numerical exceptions. In my experience, 90% of people who use floating point calculations have not the slightest idea about the possible implications.

author: NorthGuy
author_rank: Super Contributor
timestamp: January 23, 2019, 07:06:20 pm
content: Quote from: nForce on January 23, 2019, 05:55:47 pmIf a big part of your project is display, use units that you can display. For example, instead of 1.713 V use 1713 mV. It lets you do all the integers, but at the same time the display is easy - you only need to insert a dot (or a comma if you're in Europe).

author: nForce
author_rank: Frequent Contributor
timestamp: January 23, 2019, 09:16:43 pm
content: If I have 1.5 and 0.5 in a format Q5.11. How would I binary multiply these two numbers and check the result?According to wikipedia I compute 1.5 * 2^-11 and 0.5 * 2^-11. Then I multiply these two numbers, is it correct? How would I check if it's correct?

author: helius
author_rank: Super Contributor
timestamp: January 23, 2019, 10:01:16 pm
content: If it helps, you can imagine an analogous system of fixed-point decimals.Let's say that we have made our "unit" 1/1000. So the smallest representable amount is 0.001, which we can call, "1". So to represent 2, we would multiply the integer part by 10^3: "2,000". To represent 3.5, we multiply the integer part by 10^3, and then add the fraction part, which is the solution to the equation \$ 0.5 = {x\over 1000} \$; which gives us "3,500".Straight multiplying "2,000" by "3,500" will give us a product with as many digits as both multiplicands have together. So the result would be "7,000,000", which we know is incorrect: it should be "7,000". We ended up with six fraction digits, when what we needed was just three (because the unit is 1/1000). So we need to "decimal shift" right by three digits to fix that.In binary fixed point, the same is true. If we pick a unit of 1/1024 or \$ 2^{-10} \$, then we have ten binary fraction digits in each variable. The smallest representable amount is 1/1024, which we call "1". To represent 2, we multiply the integer part by 2^10, or just shift left by 10 bits:Code:2<<10 == 2048To represent 3.5, we multiply the integer part by 2^10, or just shift left by 10 bits; and add the fraction part, which is the solution to the equation \$ 0.5 = {x\over 1024} \$; which gives usCode:(3<<10) + 512 == 3584Straight multiplying "2048" by "3584" would give us "7340032", which we know is wrong, because the result should fit in 13 bits, but it takes 23. We need to binary shift right by 10 bits to fix it: "7168", which is equal to 7 * 2^10.

author: iMo
author_rank: Super Contributor
timestamp: January 24, 2019, 10:14:40 am
content: I had 32bit floating point math (incl transcendentals..) in one of my pic16f88 projects. The library was 3.5kB large, afaik. Unless you go with a tiny MCU with 16bytes ram and 512bytes flash use a built-in library, every C has got it..

author: nForce
author_rank: Frequent Contributor
timestamp: January 24, 2019, 04:18:22 pm
content: Isn't the format Q5.11 just a format of a 16-bit word, where the first 5 bits is an integer number, and rest (11 bits) is the decimal?So for Q5.11 = 1.5 and Q5.11 = 0.5, how can we write it in the Q5.11 format?

author: helius
author_rank: Super Contributor
timestamp: January 24, 2019, 04:54:58 pm
content: Quote from: nForce on January 24, 2019, 04:18:22 pmNot decimal, but binary fraction.

author: IDEngineer
author_rank: Super Contributor
timestamp: January 24, 2019, 04:56:22 pm
content: In fairness, software-based floating point can be quite reasonable. As just one example, I was doing a project based on the Microchip PIC18F and needed some FP trig. I figured I'd have to implement it in integer, but wrote and timed it (with a scope) just to see what the FP libraries could deliver. Turned out not only to be fast enough, but remarkably accurate... as good as my HP calculator for the same calculations. (Yes, I know, FP has quantization errors but this application didn't need to worry about that many digits past the decimal point.)Interestingly, the 32 bit library was FASTER than the 24 bit version. Didn't expect that.On the flip side, I definitely used integer math for the IIR filters on that same project. It's all about understanding what you're trying to do and what your hardware and libraries can deliver. You can guess, or (like me) you can run the tests and gather actual data to make an informed decision for each use case. In the example above, a slight change to the architecture helped a lot - I switched to maintaining the (frequent) intermediate data in integer form and only incurred the overhead of the FP trig operations when it was actually (and infrequently) necessary.Bottom line: Don't automatically dismiss software FP. It can be better that you expect, particularly if you architect your overall project properly.

author: dave j
author_rank: Regular Contributor
timestamp: January 24, 2019, 05:06:23 pm
content: Quote from: nForce on January 24, 2019, 04:18:22 pmAs helius said, you need to think of binary fractions. Essential Mathematics for Games and Interactive Applications has a good description of fixed point numbers and handily that chapter is available onlinehere.

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 24, 2019, 05:10:05 pm
content: Quote from: nForce on January 24, 2019, 04:18:22 pmJust multiply the number by 2^11. And make sure the integer part can be represented with only 5 bits.Eg.:1.5 -> 30720.5 -> 1024Sum: 1.5 + 0.5 -> 3072 + 1024 [you'll need a 16-bit + 16-bit -> 16-bit + carry addition] -> 4096 [which is 2 in Q5.11]Product: 1.5 * 0.5 -> 3072 * 1024 [you'll need a 16-bit x 16-bit -> 32-bit multiply] / 2^11 [a right-shift of 11 positions] -> 1536 [which is 0.75 in Q5.11]

author: nForce
author_rank: Frequent Contributor
timestamp: January 24, 2019, 05:40:57 pm
content: Quote from: SiliconWizard on January 24, 2019, 05:10:05 pmOk, thanks. So if we would see a 16-bit register where we use Q5.11 format for 1.5, then when we would convert this 16 bit register to a decimal we would get 3072? Do I now understand?

author: IanB
author_rank: Super Contributor
timestamp: January 24, 2019, 06:22:19 pm
content: Quote from: nForce on January 24, 2019, 04:18:22 pmYou have 1.5 (decimal) = 1.1 (binary), and 0.5 (decimal) = 0.1 (binary).Then you multiply them in binary (normal long multiplication): 1.1 x 0.1 = 0.11 (binary).Lastly convert back to decimal: 0.11 (binary) = 0.75 (decimal).Note:In Q5.11 format, 1.1 (binary) = 00001.10000000000 and 0.1 (binary) = 00000.10000000000When you do the fixed point multiplication in binary you will need to be aware of overflow or lost precision.

author: legacy
author_rank: Super Contributor
timestamp: January 24, 2019, 06:35:05 pm
content: fixepoint needs saturating mathmade in software or in hardwarethis, plus a fine-tuning of the algorithm.

author: 0xdeadbeef
author_rank: Super Contributor
timestamp: January 24, 2019, 07:24:23 pm
content: Quote from: legacy on January 24, 2019, 06:35:05 pmUsing saturation or not has nothing to do with fixed point arithmetic. Just because the numerical range of float types is higher compared to integer types (at the expense of gaps in the range) doesn't mean that they can't underflow (become subnormal) or overflow (infinity). The main difference is that you can detect an underflow/overflow in floatafterit happened. In a realtime system, that is of limited use though as you usually need a result that can be processed instead of just displaying an error message.Besides, you can always avoid overflow for multiplications by using numbers normalized to one. Pretty much standard in DSPs and digital filters.Quote from: legacy on January 24, 2019, 06:35:05 pmCrappy implementation with floats will lead to numerical nonsense just as with fixed point integers or whatever.

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 24, 2019, 08:52:00 pm
content: Quote from: 0xdeadbeef on January 24, 2019, 07:24:23 pmTrue. At least with fixed point, you're less tempted to think your calculations will magically work in all cases. Floating point can be deceitful.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 24, 2019, 09:07:15 pm
content: Just wanna catch the topic in here

author: legacy
author_rank: Super Contributor
timestamp: January 24, 2019, 09:30:04 pm
content: The meaning of "tuning" is that you enable the overflow/underflow circuit (or you use kind of software check) in order to verify that your numerical conditioning actually works. In my experiences, this happened with matrix computation when matrices were "bad conditioned".Yes, it would have worked bad even with floating point, but with a fixed-point, it can go even worse: during the engineering step, you need to check how good/bad the data conditioning goes, and having these checks is a good practical feature.---Being numerically tolerant.In avionics, we use dedicated video-core systems to provide navigation information to the pilot through a visual helmet that integrates a special LCD as well as we use radio-core systems to provide useful information through a kind of headphone integrated into the helmet.Both these systems implement saturation arithmetic for video and audio decoding, so as to avoid visual and sound defects.

author: legacy
author_rank: Super Contributor
timestamp: January 24, 2019, 09:31:32 pm
content: a special case is "fractional"Q(1,N-1): 1 bit for the sign, N-1 bits for the fractional part.as a reference, it's used by Microchip in the DSPic line.

author: langwadt
author_rank: Super Contributor
timestamp: January 24, 2019, 09:33:46 pm
content: Quote from: 0xdeadbeef on January 24, 2019, 07:24:23 pmalmost, -1.0*-1.0 won't fit in a Q1.xx

author: apis
author_rank: Super Contributor
timestamp: January 24, 2019, 09:50:12 pm
content: Sorry for nitpicking but FPUs don't calculate with real numbers either. Computers only do numerical computations with rational numbers (or something similar). So just use the fact that 1.071 = 1071/1000 or 1071 * 10^-3 (or something equivalent in base 2).

author: brucehoult
author_rank: Super Contributor
timestamp: January 24, 2019, 11:53:47 pm
content: Quote from: nctnico on January 23, 2019, 05:59:09 pmEven on a 16 MHz AVR, the Arduino soft floating point library takes around 10 us for a single precision floating point add, subtract, or multiply. So you can do around 100 FP operations in a millisecond. That's plenty fast enough for a lot of realtime control systems, and avoids problems with getting unexpected overflow in a fixed point calculation.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 25, 2019, 08:57:14 am
content: would somebody clarify things here for me, suppose we have 16Bit ADC and we need to do some DSP on it, how should we convert the ADC values to Q format, do the calculations and convert back the results to integers or floats.Suppose that we want to do FFTI have tested it with ARM CMSIS DSP with the Q format I would most times get wrong results when the ADC values are low, so I would mostly use the Float version, which I simply put the ADC values directly to float version and do the calculations, the point in here is how to do it fixed point so it can be done on M0 with lower CPU clock.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: January 25, 2019, 11:28:10 am
content: Consider that the 16-bit ADC values come in as 0.16 format, relative to VREF. (Or 1.15 if signed.)The numbers inside the computer need not have any units at all. Preferably, they're just more arbitrary data to pass around! You should only ever need one unit conversion step, when outputting the value to display or other* DAC.*That is, a DAC that's separate from the ADC and its VREF, in which case they probably won't exactly match.Nothing matters for DSP or FFT, as long as the gain is unity, and you don't cause overflow in intermediate steps (and, you provide enough fractional bits as needed for certain purposes -- integrators with very long time constants, or high order IIR filters, for example -- in which case the extra bits can be local to those sections, and truncated or rounded off elsewhere).On an M0, 8.24 format is probably as effortlessly good, and easy, as you'd need for most purposes? In other words, take the ADC sample, put it in a (u)int32_t, << 8, math it, then >> 8 when you're done.Incidentally, the added bits can be meaningful. For example, when accumulating values (such as a in "boxcar" FIR filter), you need ceil(Lg(N)) extra bits to hold N samples added together. Assuming uniform, independent quantization noise, we expect to gain about half as many bits in real measurement accuracy. For N = 256 say, we need 8 additional bits, and 4 of them are meaningful (effectively expanding a 16 bit ADC to 20 bits accuracy).(Or if subtractive dithering is used, nearly all 8 added bits are meaningful!)Note that uniform and independent quantization noise is not a good bet, when considering the DNL and INL of real ADCs. You'll get more resolution this way, but not necessarily more accuracy. Ideally, you'd calibrate such a system, and introduce a conversion table or piecewise or polynomial correction curve to get real numbers out. This is... heroic effort compared to the $5 to buy a "24-bit" ADC (which might only be 20 bits real accuracy, but there you have it, eh?), so it's not often used. Just FYI, know your errors and statistics.Tim

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 25, 2019, 07:07:48 pm
content: Thanks Tim for the help in here.

author: techman-001
author_rank: Frequent Contributor
timestamp: January 25, 2019, 07:30:34 pm
content: Quote from: nForce on January 23, 2019, 03:36:35 pmWith Mecrisp-Stellaris Forth, we would do it this way on a STM32F051 32 bit CortexM0 with no FPU:1,713 0,0514 f* 8 f.n0,08804819 ok.1,713 0,0514 d+ 8 f.n1,76439999 ok.