Algorithm debugging and floating point help
RussM
Posts: 27
Hey again everyone. I am having a program I am writing that uses two rotary encoders to measure size changes on a bicycle sprocket. In my code, everything works fine until the end where I want to print out the array of values in "float_highs". I've had trouble with displaying floating point values on my LCD screen in the past which makes me unsure if I have a problem with my algorithm itself or if it's just how I am utilizing the f32 functions. I have attached my code as well as a wonderfully drawn picture to help make my algorithm more clear. Thank you for any help!
{{ ┌──────────────────────────────────────────┐ │ Quadrature Decoder DEMO │ │ Author: Luke Haywas │ │ │ │ │ │ Copyright (c) <2010> <Luke Haywas> │ │ See end of file for terms of use. │ └──────────────────────────────────────────┘ Description: Demonstrates how to use my object QuadDecoder. Quadrature encoder is connected to pins 12 and 13. (If connected to different pins, simply change the value of ENCODER_PIN below) Displays value of the accumulated variable in the serial debug terminal. }} CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 '_xinfreq = 6_250_000 TX_PIN = 0 BAUD = 19_200 MS_001 = CLK_FREQ / 1_000 CLK_FREQ = ((_clkmode-xtal1)>>6)*_xinfreq LCD_PIN = 27 LCD_BAUD = 19_200 LCD_LINES = 4 LCD_COLS = 20 ENCODER_PIN_SIZE = 5 INDEX_PIN_SIZE = 6 ENCODER_PIN_ANGLE = 1 INDEX_PIX_ANGLE = 2 OBJ quad_size : "QuadDecoder" quad_angle : "QuadDecoder" LCD : "FullDuplexSerial" f32 : "F32" f32_orig : "Float32Full" fs : "FloatString" VAR long offset_size ' example variable that will be accumulated to long offset_angle long distance long temp long distance_tick long f1 long fA long angle long angle_tick byte cog long stack[90] byte highs[200] byte lows[200] long future_size long current_size long previous_size long detecthighpoint long detectlowpoint long i long j long index long index2 long float_highs[200] long float_lows[200] PUB main LCD.start(TX_PIN, TX_PIN, 00, 19_200) 'Initialize FullDuplexSerial.spin f32.start f32_orig.start LCD.tx($0C) LCD.tx($11) quad_size.start(ENCODER_PIN_SIZE, @offset_size) ' start the size encoder quad_angle.start(ENCODER_PIN_ANGLE, @offset_angle) LCD.str(string("Size =")) LCD.tx($0D) LCD.str(string("Angle =")) LCD.tx($0D) distance_tick := 0.000983 distance := 0.0 angle_tick := 0.144 angle := 0.0 current_size := 0.0 previous_size := 0.0 detecthighpoint := 0 detectlowpoint := 1 i := 0 j := 0 offset_size := 0.0 ' initialize the accumulator offset_angle := 0.0 Index := 0 repeat index2 from 0 to 199 float_highs[index2] := 0.0 repeat while (offset_angle < 2500) current_size := f32_orig.FMul(f32_orig.FFloat(offset_size), distance_tick) angle := f32_orig.FMul(f32_orig.FFloat(offset_angle), angle_tick) LCD.tx($87) LCD.str(fs.FloatToFormat(current_size,13,6)) LCD.tx($9D) LCD.str(fs.FloatToFormat(angle,10,4)) if (current_size < previous_size) AND (detecthighpoint == 1) '(previous size was a high) float_highs[i] := previous_size i++ detectlowpoint := 1 detecthighpoint := 0 if (current_size > previous_size) AND (detectlowpoint == 1) '(previous size was a low) float_lows[i] := previous_size j++ detecthighpoint := 1 detectlowpoint := 0 previous_size := distance waitcnt(clkfreq/100 + cnt) LCD.tx($0C) LCD.str(string("High Value = ")) LCD.tx($0D) LCD.tx($0D) LCD.str(string("High Value = ")) repeat index from 0 to i LCD.tx($94) LCD.str(fs.FloatToFormat(float_highs[i],13,6)) LCD.tx($BC) LCD.str(fs.FloatToFormat(float_highs[i+1],13,6)) pause(2000) LCD.tx($0C) quad_size.stop quad_angle.stop PUB pause(ms) | t t := cnt repeat ms waitcnt(t += MS_001) {{ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ TERMS OF USE: MIT License │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ │files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ │modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ │is furnished to do so, subject to the following conditions: │ │ │ │The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ │ │ │THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ │WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ │COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ │ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }}
Comments
The only comparisons that works with floats are "==" and "<>".
You'll want to use the "Cmp" method of F32 to do your comparisons.
Believe it or not, integer comparison does work for floating point variables. Sign is the most significant bit, then comes the exponent and the mantissa is stored in the low order bits. So even though the interpretation of the bits is different the order is correct. That means if floating point variable a is higher than b then the expression a > b for their integer representation is always true. I use this frequently in my code and never had problems.
I am thinking it has something to do with my array of floating point values. As a test I tried filling the first 11 elements of floats_high with a random floating point value and simply printing it out to the screen. Alas, I still get a read out of 0.0000 for each value.
EDIT: I replaced the FloatToFormat call with FloatToString and now it seems to print the values fine. However after making that change into my algorithm I am still getting odd results, so the error must be somewhere in my logic.
It's true. As noted above. Comparing floats using signed integer comparisons, less than, greater than, works just fine. Edit: NOT QUITE TRUE, see below in thread.
I just wanted to add that this is not an accident. The designer of the IEEE 754 floating point standard. Professor William Kahan went to some effort to make sure that was the case. The idea being that if you can compare floats with integer ops that allows much faster sorting algorithms, like radix sort.
I occasionally get PMs asking for help. I usually tell them I'd rather help in the forum so if I say something wrong I'll be corrected. I very much appreciate the correction.
Now I wonder why the floating point objects have a "Cmp" method.
That's a good question.
Presumably the cmp method returns -1, 0 or 1 depending how the comparison went. It's does start to look like a long winded way of doing it.
I think you'll find it only works if at least one variable is positive. When comparing two negative floating point numbers the integer comparison has to be reversed. For example, -1.0f is 0xbf800000, -2.0f is 0xc0000000; but while 0xbf800000 < 0xc0000000, -1.0 > -2.0.
Eric
Besides the issue with comparing two negative numbers, there are also some corner cases (like infinities, NotANumbers, and -0) that also need to be considered when comparing floating point numbers.
Eric
Now I'm back to being confused. Which, I suppose, is better than being confidently wrong.
Thanks for the continuing education Eric.
Your original statement ("use the CMP method to compare floating point values") was correct, and is good advice. That's the way to compare floats. It's best to use CMP even for testing for equality or inequality. For example, the bit patterns 0x00000000 and 0x80000000 are different, but according to the IEEE standard they should compare as equal (they stand for 0.0 and -0.0, respectively). CMP handles this kind of thing.
An even simpler way to use floating point is to do it in C rather than Spin, but that's a whole different topic :-).
Eric
Dammit, you are right. You cannot compare floats using integer representation directly. But it can be done.
My little mistake is that the integer representation of floats is ordered correctly provided you are treating your ints as signed magnitude format. Of course most computers now a days use two's complement so it only works if one or more of the floats is positive.
What you have to do is convert your negative numbers to two's compliment and then compare them using normal signed integer operations. This is done by simple subtracting the negative numbers from hexadecimal 80000000. Might look like this in Spin.
As mentioned in this thread we may have problems here with NaNs and infinities, but really I think that is not an issue. If your code has such things cropping up in it you probably have bigger problems to deal with.
WARNING: NEVER COMPARE FLOATS FOR EQUALITY.
Sorry for shouting but this comes up a lot. In general floats are never equal when you might expect them to be. The classic example is this:
(0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1) == (10 * 0.1)
is false. Basically all to do with rounding errors. If you really want to understand the problems with comparing floats a good place to start is here:
http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
My old boss used to say to all new team members on a radar signal processing project "If you think you need floating point maths to solve the problem then you don't understand the problem."
I always though he meant that you can do most things with fixed point arithmetic if you think about it enough and the floating point was just the lazy way out.
Thirty years later I realize that his statement has another meaning, if you really do need floating point to solve your problem then you won't understand your problem any more! Reason being that floating point is hard and takes a lot of effort to understand all it's subtleties. As my error above shows:)
Thanks,
So, if you happen to want to sort a bunch of floats then converting them to ints first will make it go faster and you can convert them back easily when done. I think it's kind of neat.