Question:
I want to scale a number in C programming using only integer arithmetic. What is the best way to do it?
John Samarasinghe
2012-10-15 13:43:55 UTC
There cannot be any floating point numbers involved.
Four answers:
Jonathan
2012-10-15 15:12:00 UTC
It sounds very much like you have a 10-bit ADC and you need to process that data on an 8-bit microcontroller with C code. It also sounds (because you say "I have 2 variables, x and y") like you may be taking two different 10-bit ADC values from two different sensors (for example) and need to process some kind of ratio between those two values as a fraction you know, a priori, to be between 0 and 1.



But I've no real idea. I'm just guessing because you haven't provided EXACT details of what you are trying to do.



Please provide details about the input(s) you have and final output computation result you need (and it's meaning -- what is it, itself, for?) The expression, x ⁄ y⋅256, is nice enough. But it doesn't say anything about the range of x, y, or their relationship to each other. And these details, and more, are important to know. What are you reading in, what do those values represent, how do they relate to each other, and what are you trying to get as a final computed value?



Oh. And what microcontroller is this? And whose compiler?



EDIT:

This is so much better, John. It makes a lot of sense now, too. I'll use my imagination and imagine a button available to take a reading of the ambient light and that from then on the display shows the ratio from there, at 100% or less. (You don't say what it should do if for some reason the 'x' variable should be greater than 'y', but I'll let you worry about that detail.) The ⨯256 is then (I assume) just your attempt to come up with a number that is between 0 and 255, expressing 0% to ²⁵⁵⁄₂₅₆{≈ 99.6%} and get the maximum resolution you can?



And it makes sense now why you do NOT want to haul in a floating point library. It's big, it's slow... and it may be buggy, too. You just want a simple way to do a division without using floating point or, for reasons I'm not entirely sure about, the C18 support for larger integers (even 24-bit as described in Section 4.7 of the "MPLAB C18 C Compiler Libraries" manual.)



The simplistic approach would be to use a 16-bit integer division. You'd shift your 'x' value leftwards so that the 8 bit value sits in the most significant byte. (Just use << 8, I suppose.) Then you would perform a division of this result, by y, using the usual C compiler's choice for whatever it uses for 16-bit integer division. Since you know, a priori, that y > x, the result will fit into 8 bits because if y > x then x ⁄ y < 1 so it must be that (256⋅x) ⁄ y < 256.



But it isn't very good in terms of preserving your precision. Best answer would be to keep your 10-bit values (perhaps using averaging if the noise is Gaussian shaped and you have a couple of noise bits present, at least, to even improve upon that) and then "normalize" your x value so that the uppermost bit is ALWAYS a 1, do the same for your y value too, and then use a very simple division algorithm that you write to calculate exactly what you want from the two of them. In this way, you maximally preserve precision and you only compute the bits you actually need to have. It's very fast this way and at the same time retains the best possible precision and monotonicity of results.



I can probably help you (I mean freely, as in no cost.) I've written many integer and floating point division routines before. And I have programmed on the PIC18 before, as well. But you will perhaps have to write me directly. It's easier if we just interact and get this solved quickly. And I need to understand better why you aren't just using the existing 16-bit division code that is in the library already. (And ask some application questions about the ADC signal, it's S/N, the noise bits, and the like.)
husoski
2012-10-15 14:28:07 UTC
If you want to scale an int up by a whole factor, that's just simple multiplication. Assuming n_old, n_new and scale are all int values (or long works too):



n_new = scale * n_old;



To scale down by a whole factor (1/2, 1/3, 1/4, etc.) that's division...but you should usually round. You can do that by adding half the divisor prior to division.



simple scale: n_new = n_old / scale;

rounded scale: n_new = (n_old + scale/2)/scale;



If you want to scale by a rational fraction p/q, where p and q are ints (q is nonzero) then combine the two:



simple: n_new = n_old*p/q; /* Note that multiplication happens first. n_old/q*p is WRONG */

rounded: n_new = (n_old*p + q/2) / q;



You need overhead to make that work. If n_old > (INT_MAX - q/2)/p then you will get overflow in the numerator before scaling down by q. This sort of computation is uses a lot on smaller microcontrollers. q is usually chosen to be a power of 2 so that the division can be done with a right shift.



@Graham: That \ for integer division works in Visual Basic, but not in C. In C, when both arguments are integral types, integer division is automatically performed. Unlike VB or Python 3 (which uses // instead of \), there is no operator in C that does integer division of floating point numbers.
2012-10-15 14:00:29 UTC
More information would be nice, but a place to start might be using multiplication and integer division, for example \ for division instead of /



What type of numbers are we talking about and how much to scale them by



another option may by using shift operators >> 1 is the same as divide by 2



EDIT:



To scale from 10bit to 8bit, just use newvalue=value>>2;



Analog to digital converter?



EDIT 2:



Would the rounded value of x/y be useful, the PIC is my home turf (although I MUCH prefer C30, and the PIC24s), and I would do it using something like ratio=(int)x/y;
hockenberry
2016-12-12 14:55:18 UTC
different than for ItachisXeyes suggestion, I have not any thought. it rather is like attempting to force a vehicle devoid of wheels. AND, OR and not are logical operators. they are in a position to't be used for arithmetic. Bit shifting will paintings merely multilplying integer values via powers of two. you may not do addition, subtraction or any multiply or divide that's not potential of two, and you are going to be able to't function on floating factor values.


This content was originally posted on Y! Answers, a Q&A website that shut down in 2021.
Loading...