Working with Decimal values
JBWolf
Posts: 405
in Propeller 1
I am having a terrible time time to figure out how to work with decimals.
I purchased one of the MS5607 Altimeters, which came with example code... but its completely formatted to displaying string output.
I can get a value from the altimeter driver, which returns a non-decimal (i.e. 87594 = 875.94ft), by doing this:
CM := alt.altitude(alt.average_press) ' Get the current altitude in cm, from new average local pressure.
ST := alt.convert(CM, alt#FEET) ' Record starting elevation, use alt.convert to change CM to FT
but if I try to divide by 100 or multiply by 0.01, it drops the decimal value and 87594 becomes 875 instead of 875.94.
I have tried using local var, byte, long and float.... I revisited the PEkit book and tried floatmath with fmath.fmul and fmath.fdiv. but those return a crazy value.
I could just use the non-decimal value, but I find it odd that I cannot work with or display a remainder.
What am I missing?
I purchased one of the MS5607 Altimeters, which came with example code... but its completely formatted to displaying string output.
I can get a value from the altimeter driver, which returns a non-decimal (i.e. 87594 = 875.94ft), by doing this:
CM := alt.altitude(alt.average_press) ' Get the current altitude in cm, from new average local pressure.
ST := alt.convert(CM, alt#FEET) ' Record starting elevation, use alt.convert to change CM to FT
but if I try to divide by 100 or multiply by 0.01, it drops the decimal value and 87594 becomes 875 instead of 875.94.
I have tried using local var, byte, long and float.... I revisited the PEkit book and tried floatmath with fmath.fmul and fmath.fdiv. but those return a crazy value.
I could just use the non-decimal value, but I find it odd that I cannot work with or display a remainder.
What am I missing?
Comments
a= 12345
b=0.01
c=a*b
pst.dec(c)
just havent figured out how to work with and display decimals.
Edit: Oh, yeah! Forgot about the left padding zero(es) for the fractional part. See the comments below for the proper solution.
To add or subtract fixed-point numbers, they need to have the same multiplier - scale one or both until they do. To multiply two numbers with a multiplier of 100, say 12.34 (stored as 1234) and 23.4 (stored as 2340), you multiply them (1234 * 2340 = 2887560) to get a number with a multiplier that's the product of the two input numbers: (12.34 * 100) * (23.40 * 100) = (288.7560 * 10000). If you want the result to have the original multiplier, you just divide the 2887560 by 100, which gives 28875 (printed as 288.75, which is the (truncated) correct answer). Watch out for overflow - divide before multiplication if you need to. You can split the division into two - one before, one after - to maximize precision while avoiding overflow.
EDIT: use Spin modulus operator
I cannot get any function out of a percentage sign... i.e.
x:=x%100
pst.dec((x % 100))
EDIT: (My copy of) the PST object doesn't have a way to print fixed-width decimal values, so you'll either have to prepend a "0" if ||x // 100 < 10, or you could just do the two digits manually separately:
Note the ||absolute value operators. You need these, or negative numbers won't display properly.
It might be a good idea to make a function to do this for you, since the chances are that you'll be doing it in multiple places.
I have a product that displays values to two decimal points. For it I have a little method called dec2() that ensures I get a 2-digit (with leading zero) value.
Easy-peasy. Now I would do this: and get the correct display of 10.06.
A main loop to read and display pressure and temperature, lined up in columns with 2 decimal places becomes:
It expands upon the Library's SimpleNumbers object and provides methods for fixed-point decimal conversions to strings. For example, to print 1234 as 12.34, you might write:
where ser is the serial output object, like Parallax Serial Terminal, and num refers to my object. This prints 12.34 in a field 6 spaces wide, with 2 digits after the decimal point. SimpleNumbersPlus includes a buffer that allows multiple string results before the buffer has to get reused.
I believe that including methods like dec and hex in I/O routines is redundant. It's better if output drivers were limited to char and str and that numerical conversions happened in external objects that convert numbers to strings in various desired formats.
-Phil
the // worked beautifully for basic display.
Many other great responses here, I'll be saving this thread. thanks much guys!
now if I can just figure out why the altimeter keeps flickering between 0.00ft and exactly 0.23ft while sitting still
Phil,
About redundancy. On the side of including those methods in the I/O objects, I always need to debug results, most often to a serial terminal and in fixed point decimal, so I want the quickest path without additional objects and extra keystrokes. Often that suffices. In more complex projects that involve sending data to different end points (terminal, wireless, LCD, TV, SD card etc.) it clearly becomes more efficient to keep the numerical conversions as a separate object. In that case the unused methods in the I/O object can be excised or simply not compiled per bst or PropellerIDE.
I wrote my own separate object, I call "recordBuilder", that acts like your buffer to build up fields into a record . (We like to invent our own wheels!). It includes the numerical methods in a range of formats, with field separators and means to insert status codes like NA and OL. The snippet I posted above has the handler for correct display of NEGX, but in recordBuilder, NEGX passed in from sensor driver converts to "NA" and POSX converts to "OL".