PDA

View Full Version : Bug in Float32 pow function



Phil Pilgrim (PhiPi)
12-14-2009, 04:53 AM
I've been using the Float32 object (v1.4) in an effort to write an object for transforming among several color spaces. I was getting some really bizarre results, which I've finally tracked down to the pow function, which raises a floating point number to a floating point power. The following program demonstrates the bug:




CON

_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000

OBJ

sio : "FullDuplexSerial"
f : "Float32"

PUB start | i, y

f.start
sio.start(31, 30, 0, 9600)
waitcnt(cnt + clkfreq/10)
repeat i from 0 to 255
y := f.fround(f.fmul(f.pow(f.ffloat(i), 0.3333333), 1000.0))
sio.dec(i)
sio.tx(",")
sio.dec(y)
sio.tx(13)




It computes the function y = round(i1/3 * 1000) for i from 0 to 255. Here's a graph of the output, plotted in Excel:

http://forums.parallax.com/attachment.php?attachmentid=65847

I didn't plot the value for i == 0, since pow returned NaN, which is incorrect for positive powers: it should return zero.

-Phil

Post Edited (Phil Pilgrim (PhiPi)) : 12/13/2009 10:35:11 PM GMT

ElectricAye
12-14-2009, 06:07 AM
Thanks, Phil.
Just curious: so how do bug fixes for problems like this get updated in the OBEX?

Phil Pilgrim (PhiPi)
12-14-2009, 06:12 AM
The last time there was bug report for Float32, Chip took note of the thread in the forum and emailed Cam Thompson, who fixed the problem fairly quickly.

-Phil

Bergamot
12-15-2009, 09:48 AM
Is it possible that the discontinuities are the result of the float switching from one exponent to another?

kuroneko
12-15-2009, 11:13 AM
@Phil: That's odd. I just grabbed 1.4 from the OBEX and also tested 1.5 (part of PropTool library). They both give me the same result. Two issues though:
returns NaN for i = 0
glitch for i = 8
http://forums.parallax.com/attachment.php?attachmentid=65885

Post Edited (kuroneko) : 12/15/2009 4:19:51 AM GMT

Phil Pilgrim (PhiPi)
12-15-2009, 11:49 AM
I've contacted Cam, and he promised to look into it this evening. We'll see what he comes up with.

-Phil

Phil Pilgrim (PhiPi)
12-15-2009, 12:16 PM
kuroneko,

I've just confirmed your results. I had version 1.3 in my work directory that was preempting version 1.4 in the library directory. (Grrr! This is just one more example of why a better way needs to be found to specify object locations!) So there are still the two issues you pointed out.

-Phil

VIRAND
12-15-2009, 02:35 PM
Could the error be a sum of rounding error and lack of precision?
BTW. Also... 0.33333 doesn't come close to the phenomenon of 0.99999999999999=1.
So I'd expect those glitches.

I don't use floats though, I use arbitrary precision integers or fixed points,
since I need to count somethings like fleas and haystacks and needles at the same time,
and also calculate things like 1-(1/googol) which look like 0.99999999999....989999999999999...

Edit:Pop Quiz:What is the meaning of the number that equals:
N:=(10**300) / ((10**30)-(10**3)-1)
It can fit on a Propeller video screen, which if 30 columns wide, will have a big surprise obvious meaning.
Hint:It is a "Finite Probability Drive" and it equals some very useful and interesting data.
But it IS a BIG NUMBER. A Very Long Division:
How many times does 99999999999999999999999998999 go into a googol cubed ?
Edit2:I suppose the interestingness of the information depends on whether you think it is
worth one division, OR many additions, OR tons of multiplications; and that digital data can be reduced to arithmetic.

Post Edited (VIRAND) : 12/15/2009 10:09:25 AM GMT

Phil Pilgrim (PhiPi)
12-15-2009, 03:38 PM
Cam has verifed that there is an issue with the exp2 function that pow uses and that he will look into it Tuesday, along with the 0+n == NaN result.

After expunging the old Float32 from my work directory, I retried some of the colorspace transformations I was working on and didn't notice any anomalies. So I guess I "screwed up good", as they say. If the old Float32 hadn't been asserting itself, I (or I should say Kuroneko) would never have found the bug in the newer one.

-Phil

Phil Pilgrim (PhiPi)
12-15-2009, 03:53 PM
VIRAND,

I'm with you on avoiding floating point when possible, and this was my first experience with the Prop's floating point library. But when I saw that the RGB-to-CIELAB transformation required fractional powers, I just threw up my hands. It was less effort to do it in floating point than to concoct the necessary fixed-point log and antilog functions from scratch.

After squinting at nested method calls (and counting parentheses) for a couple days, though, I find myself yearning for a compiler that would recognize and keep track of floating point operands and results on the stack, so that traditional infix notation could be used instead. A new variable type, flot, would not be so far-fetched either.

-Phil

VIRAND
12-15-2009, 07:33 PM
Phil Pilgrim (PhiPi) said...
VIRAND,

I'm with you on avoiding floating point when possible, and this was my first experience with the Prop's floating point library. But when I saw that the RGB-to-CIELAB transformation required fractional powers, I just threw up my hands. It was less effort to do it in floating point than to concoct the necessary fixed-point log and antilog functions from scratch.

After squinting at nested method calls (and counting parentheses) for a couple days, though, I find myself yearning for a compiler that would recognize and keep track of floating point operands and results on the stack, so that traditional infix notation could be used instead. A new variable type, flot, would not be so far-fetched either.

-Phil

That reminds me of when I tried to make a color-associative memory, when blue and RGB LEDs were new, and with a
12 bit ADC on its photodiode eye, I defined a cone which was axial from its black zero point, to white on the cone base center,
and hue was angle, and saturation was distance from gray axis. It was trained by name and color sample, but failed in
two ways. It hopelessly couldn't tell black from navy blue (and neither can I) but worse, it hopelessly couldn't tell
the difference between "Purple" and "People". Didn't matter what skin color looked like, the machine had great coincidence
in the regions of the cone when purple or people were "seen" by it. I suppose that it had the same flaw as original NTSC TV,
which often made people look like Martians or the prey of the "Purple People Eater" (some old pop song). Of course I
imagined the NTSC color system when I defined the color cone. I don't remember how they fixed NTSC to make people
the right color, but there are certain anomalies in human vision involved, such as monochromatic yellow light causing
"black and white" vision, changing to red and green lights can produce a bizarre effect as if reality was alternating
between black and white and full color, a pair of B&W slide projectors can create full color illusion if red or cyan filter
is put on one of the projectors (and the slide was taken with the same filter), but most relevant to the purple
people problem is that purple stimulates red and blue in human eyes but the blue LED of the time was just clear
of cyan wavelength, so it had no real purple in it. I think some early VCR cameras had only two ... vidicons? ... ,
and exploited an illusion of full color with either red and cyan or B&W plus one of those colors. The range of colors
made by red and green may be sufficient to make a cheap 3D color image projector while RGB LED dot matrix
from China still cost much more than the RG ones used in news-stock-ticker-scroller-signs. RG includes orange,
yellow, lime, brown while blue only adds purple, and the mostly red-pink magenta and the greenish cyan which are
not very important colors nor as distinct as the RG range.
I started to wonder a little bit if the Parallax color sensor is a lot better than the 12 bit photodiode and the 1992 RGB LED,
so that it can tell the difference between people and purple, but I didn't get one yet just for curiosity.
I hope you don't have to struggle hopelessly with the purple people problem in your project. I looked up the LAB
color map and don't see it as any easier than the RGB --> "Color Cone" map, which I managed to do with powers of 2
degrees in the math, and it was integer math, with quasi-sine and arctan tables (256 degrees not 360), on a 6800,
which was Not great for math with only 8-bit add and subtract, and it seems so gone now.
All I can remember about fractional exponents is the interpretation of the numerator as a power and denominator
as a root. In integer there is a simple pattern for roots and the algorithm for powers is like the algorithm for multiplication,
but by changing shift and add to shift and multiply. The roots problem can be like long division also, but there is an
easy pattern, for example integer Square roots can be done with a small loop that uses the pattern
(1)+3=(4)+5=(9)+7=(16)+9=(25)+11+(36)+13=(49)+15=( 64)+17=(81)+19=(100)...
and interpolate for more precision. Cubes/roots? maybe... (1)+7=(8)+19=(27)+37=(64)...nope, 3D stuff is
going on there and I'm too tired to think about 4D now.

Phil Pilgrim (PhiPi)
12-16-2009, 12:09 AM
VIRAND,

It sounds like you've trod this path before! And, yes, I do remember the "Flying Purple People Eater", which came out about the same time as "Witch Doctor". http://forums.parallax.com/images/smilies/smile.gif

How this all started was a need to convert the millions of RGB colors sensed by the ColorPAL to the "closest" of the Propeller's 64 native VGA colors as a quick demo. On the PC, with its 2563 RGB display colors, it's no problem getting an accurate representation from the ColorPAL without having to measure distance in some weird space. But mapping those 16M sensed colors into 64, to display colors directly from the Prop that at least "look like" the one being sensed has proven to be a challenge. I tried converting to HSV first, and measuring distance there, but to no avail. The CIELAB distance function gives much better results, but human vision seems much more sensitive to tint than the CIELAB space can account for. For dark colors, even when the RGB coordinates clearly indicate some discernable saturation, the nearest CIELAB VGA color might be gray. (It was the opposite problem in HSV space.) I think what I will have to do is take RGB readings from all the color chips I have and map those into the colors I want displayed, then run an optimization program for weighting two of the three CIELAB dimensions in the distance function so as to produce those results. I also need to factor in the VGA display's gamma transformation, which I have not yet done. If that doesn't work, I guess I'll have to invent my own color space.

-Phil

lonesock
12-16-2009, 12:57 AM
If you are up for trying a different color space, try YCoCg: wiki.multimedia.cx/index.php?title=YCoCg (http://wiki.multimedia.cx/index.php?title=YCoCg). It's used in video game circles as it's easy to use, compresses well, and has some properties similar to the YCbCr color space.

Jonathan

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
lonesock
Piranha are people too.

camt
12-17-2009, 03:33 AM
I sent an updated version of Float32 to Phil yesterday for testing. Once I hear back from him that it looks OK, I'll post the new version to the object exchange. I've also attached the new file to this message if anyone else wants to try it out.
-Cam

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Cam Thompson
Micromega Corporation

camt
12-17-2009, 03:41 AM
I should have provided more detail in the last message, I'm sure some of you are curious.

The problem was in Exp2. There's a spot in the code where a fractional value was assumed, but if the fraction was one bit less less 1.0 a 1/2 bit rounding (which is intended) causes it to become 1.0, so the fractional value became 0 instead of 0.99999999. Pow(8,0.3333333) became 1.0 instead of 2.0. The rounding is now properly accounted for.

I also fixed the pow(0, n) case. Since infinity special cases are not supported in Float32 due to the limited memory available in a cog, pow(0, n) returns 0 regardless of the sign of n.
-Cam

VIRAND
12-17-2009, 06:01 PM
So you are converting RGB to RGB? Except for the influence of gamma, the solution would be simply discarding all
except the two MSB of each. Sounds too easy but intermediate color space sounds totally unnecessary. You just
have to estimate the high-color values of either only the 2 bits in each channel, or the approximate high-color threshold
for the 3 non-black levels of each primary color. Obviously it is unlikely you are getting RGB inputs whose
highest 2 bits mean exactly the same as Propeller VGA 2-bit color channels, but IF you were just reducing the
color depth of a digital RGB monitor (such as certain Laptop and PDA LCDs), you would only use the highest bits.

Let the Propeller sample its 64 colors and store only inputs for those, and really you have no more choices when you
sample other colors but the nearest or lower PropVGA color's sampled value to determine which PropVGA color to use.
If the results are often off the target, allow more bits to be sampled, maybe 256 colors, but actually only 64, and
massage the regions that are not correct, nudging their boundaries toward the better choice. In other words, if the
256 color map of 64 colors starts out like a quilt, when the really bad colors are corrected, it will look perhaps more
like a jigsaw puzzle with its necessarily tweaked irregular boundaries.



Phil Pilgrim (PhiPi) said...
VIRAND,

It sounds like you've trod this path before! And, yes, I do remember the "Flying Purple People Eater", which came out about the same time as "Witch Doctor". http://forums.parallax.com/images/smilies/smile.gif

How this all started was a need to convert the millions of RGB colors sensed by the ColorPAL to the "closest" of the Propeller's 64 native VGA colors as a quick demo. On the PC, with its 2563 RGB display colors, it's no problem getting an accurate representation from the ColorPAL without having to measure distance in some weird space. But mapping those 16M sensed colors into 64, to display colors directly from the Prop that at least "look like" the one being sensed has proven to be a challenge. I tried converting to HSV first, and measuring distance there, but to no avail. The CIELAB distance function gives much better results, but human vision seems much more sensitive to tint than the CIELAB space can account for. For dark colors, even when the RGB coordinates clearly indicate some discernable saturation, the nearest CIELAB VGA color might be gray. (It was the opposite problem in HSV space.) I think what I will have to do is take RGB readings from all the color chips I have and map those into the colors I want displayed, then run an optimization program for weighting two of the three CIELAB dimensions in the distance function so as to produce those results. I also need to factor in the VGA display's gamma transformation, which I have not yet done. If that doesn't work, I guess I'll have to invent my own color space.

-Phil

Phil Pilgrim (PhiPi)
12-18-2009, 12:43 AM
VIRAND,

You're right about not needing to gp RGB -> ??? -> RGB, but the solution is not as simple as just using the top two MSBs of the sensed color. What I ended up with was a voting method that was taken over a gradual change in intensity from 0.75 to 1.25 of the sensed value. You can read about it here:

http://forums.parallax.com/showthread.php?p=865569

-Phil

camt
12-21-2009, 10:54 PM
An updated version of the floating point routines (V1.6) has been posted to the OBEX. It fixes the glitch in the Pow (and Exp) function and corrects the special cases in Pow for base = 0.
0**-n returns Nan
0**0 return 1.0
0**+n return 0.0
If you loaded the Float32 routine posted earlier in this thread, please reload from the OBEX, as the special cases for Pow base=0 changed.
-Cam

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Cam Thompson
Micromega Corporation

Phil Pilgrim (PhiPi)
12-22-2009, 12:31 AM
Thanks, Cam!

-Phil