Oh, and I see another bug in the output of my celsius-to-fahrenheit converer program, that I posted about here a few minutes ago (re. extra blank lines form printf).
Where is 41.000 ??? This table should have 6 entries, not 5, and the last line should read:
41.000 105.800
Looks like the <= comparison is failing when Cels gets up to 41.000. I'm guessing it's somehow gets off a very small amount. Perhaps it's 41.00000001 when it gets compared to 41, so <= return false.
I could change "Cels <= CelsMax" to "Cels <= (CelsMax+0.0001)". But that's a bit ugly. Any simpler way to do this?
-- Cheers, Robbie Hatley lonewolf at well dot com www dot well dot com slant tilde lonewolf slant
"Robbie Hatley" <see.my.signat...@for.my.contact.info> writes: > Oh, and I see another bug in the output of my > celsius-to-fahrenheit converer program, that I posted about > here a few minutes ago (re. extra blank lines form printf).
> Where is 41.000 ??? This table should have 6 entries, not 5, > and the last line should read:
> 41.000 105.800
> Looks like the <= comparison is failing when Cels gets up to 41.000. > I'm guessing it's somehow gets off a very small amount. Perhaps > it's 41.00000001 when it gets compared to 41, so <= return false.
> I could change "Cels <= CelsMax" to "Cels <= (CelsMax+0.0001)". > But that's a bit ugly. Any simpler way to do this?
Yes - count up in integers and work out the floating point values for printing. But if ugly concerns you, look at those variable names, capitalisation conventions and our long recent thread on testing integers to see if they still equal variables. -- Online waterways route planner | http://canalplan.eu Plan trips, see photos, check facilities | http://canalplan.org.uk
> Oh, and I see another bug in the output of my > celsius-to-fahrenheit converer program, that I posted about > here a few minutes ago (re. extra blank lines form printf).
"Nick" wrote: > ... count up in integers and work out the floating point values for > printing...
Ok, could do that. In other programs. In these two, I'm feeling lazy. Just got them to work using the "+0.0001" thing. I admit that the "integer" thing sounds like the technically better way of doing it.
> ... look at those variable names ...
Hey, at least I didn't name them:
int a,b,c,d,e,f;
I've known programmers that do that. In production code, for mid-sized corporations, no less. Grrr!
> ... capitalisation conventions ...
For some reason, Piers Anthony comes to mind. I see a flock of birds, each shaped like a small letter c, rising from the ground in front of me. I step back, startled, but my programmer friend beside me says, "don't be alarmed, it's only a flock of small C-gulls on their way to a Capitalization Convention".
-- Cheers, Robbie Hatley lonewolf at well dot com www dot well dot com slant tilde lonewolf slant
On 2010-03-24, Robbie Hatley <see.my.signat...@for.my.contact.info> wrote:
> Oh, and I see another bug in the output of my > celsius-to-fahrenheit converer program, that I posted about > here a few minutes ago (re. extra blank lines form printf).
No, you don't. :)
> for (Cels = CelsMin; Cels <= CelsMax; Cels += CelsInc) > { > Fahr = (((Cels*180.0)/100.0)+32); > printf("%10.3f %10.3f\n\n", Cels, Fahr); > } > 41.000 105.800 > Looks like the <= comparison is failing when Cels gets up to 41.000. > I'm guessing it's somehow gets off a very small amount. Perhaps > it's 41.00000001 when it gets compared to 41, so <= return false.
Right.
> I could change "Cels <= CelsMax" to "Cels <= (CelsMax+0.0001)". > But that's a bit ugly. Any simpler way to do this?
It's surprisingly hard. Basically, floating point numbers generally can't represent values that aren't exact multiples of a power of two. "0.2" is not exact, so addition of "0.2" repeatedly is also not exact.
If you really need to do this, do all the loop controls in integer math, then convert at the last minute using a fixed scale factor. e.g., loop up to 205, then divide by 5 inside the loop.
> > Where is 41.000 ??? This table should have 6 entries ...
> In light of the 0.001 increment, I'd have expected about > a thousand lines. So I guess `+=' is also broken, huh?
That was a typo in my post. I should have typed, "celsfahr 40 41 0.2". Dunno why I wrote 0.001. That's the minimum increment the program allows, so I probably had that on my mind.
The <= issue is also now fixed. I used a +0.0001 "fuzz zone". (See other post.)
-- Cheers, Robbie Hatley lonewolf at well dot com www dot well dot com slant tilde lonewolf slant
"Robbie Hatley" <see.my.signat...@for.my.contact.info> writes: > Oh, and I see another bug in the output of my > celsius-to-fahrenheit converer program, that I posted about > here a few minutes ago (re. extra blank lines form printf). ... > for (Cels = CelsMin; Cels <= CelsMax; Cels += CelsInc) > { > Fahr = (((Cels*180.0)/100.0)+32); > printf("%10.3f %10.3f\n\n", Cels, Fahr); > } ... > 40.800 105.440
> Where is 41.000 ??? ... > I'm guessing it's somehow gets off a very small amount. Perhaps > it's 41.00000001 when it gets compared to 41, so <= return false.
Don't guess; read: /What Every Computer Scientist Should Know About Floating-Point Arithmetic/ by David Goldberg
Phil -- I find the easiest thing to do is to k/f myself and just troll away -- David Melville on r.a.s.f1
>>> Where is 41.000 ??? This table should have 6 entries ...
>> In light of the 0.001 increment, I'd have expected about >> a thousand lines. So I guess `+=' is also broken, huh?
> That was a typo in my post. I should have typed, > "celsfahr 40 41 0.2". Dunno why I wrote 0.001. That's the > minimum increment the program allows, so I probably had > that on my mind.
Based on a large study (sample size N=2), there's some indication that you find it difficult to make an accurate problem report. You might want to think about ways of improving your accuracy, as there are situations where an inaccurate report can be life-threatening. "Doctor, I have this terrible pain in the diodes on my left side." "Nothing to worry about, Robbie, just take two aspirin and call me in the morning. If you'd said the *right* side, I'd have been seriously worried -- but on the left, I'm quite sure it's nothing to fret about. Have a nice life!"
> The<= issue is also now fixed. I used a +0.0001 "fuzz zone". > (See other post.)
Yeah. As others have explained, this is not a good solution. (Given your remarks about having seen bad things "in production code," I'm a little disturbed that you didn't know this already. No offense meant: We all start out ignorant. But preserving one's ignorance is not a virtue, and if you're dealing with "production code" I urge you to cast your ignorance aside ASAP. Please.)
"Robbie Hatley" <see.my.signat...@for.my.contact.info> writes: > Oh, and I see another bug in the output of my > celsius-to-fahrenheit converer program, that I posted about > here a few minutes ago (re. extra blank lines form printf).
> Where is 41.000 ??? This table should have 6 entries, not 5, > and the last line should read:
> 41.000 105.800
[...]
In addition to the other advice you've gotten, it might be instructive to print the values to greater precision. Since you stated a new thread, I can't find the article containing your actual code without more effort than I'm willing to expend. But here's a program based on the snipped above, which allows you to vary the precision of the output via a command-line argument. Try it with increasing values of "digits". On my system, things start to get interesting around digits==14.
The best pieces of advice you've gotten so far are:
Control your loop with an integer, not a floating-point variable.
Read "What Every Computer Scientist Should Know About Floating-Point Arithmetic" by David Goldberg.
-- Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst> Nokia "We must do something. This is something. Therefore, we must do this." -- Antony Jay and Jonathan Lynn, "Yes Minister"
>> Note the two count them 2 count them again II zwei due >> deux newline characters ... You asked for blank lines, you >> got blank lines -- well, duh.
>Yep, that time I quoted correct source. (See my post, >"Oops, wrong source file!".) That problem solved.
>> > ... and the output for "celsfahr 40 41 0.001" is ...
>> > Where is 41.000 ??? This table should have 6 entries ...
>> In light of the 0.001 increment, I'd have expected about >> a thousand lines. So I guess `+=' is also broken, huh?
>That was a typo in my post. I should have typed, >"celsfahr 40 41 0.2". Dunno why I wrote 0.001. That's the >minimum increment the program allows, so I probably had >that on my mind.
>The <= issue is also now fixed. I used a +0.0001 "fuzz zone". >(See other post.)
It is not fixed. You have simply camouflaged it. You have managed to obtain the output you want with no understanding of what the real issue still is!
What is the true value of CelsInc? Hint - it is not numerical value 0.2 that you think it is.
> > The <= issue is also now fixed. I used a +0.0001 "fuzz zone".
> It is not fixed.
Oh, but it *is* fixed.
> You have simply camouflaged it.
No, I "fuzzed" it. Read on.
> You have managed to obtain the output you want
Precisely. By adding a "fuzz zone" of 0.00001 in a program where the numbers are increasing in increments of at least 0.001 (100 times larger), I've "fuzzed-out" (or "overloaded", in C++ lingo) the meaning of operator "<=" to mean "less-than OR approximately-equal-to-give-or-take-0.00001", which cures the problem in the most-direct and simplist way.
This "fuzz zone" approach can work well with much less code bloat than the "convert-to-integers" method, PROVIDED that you can find a fuzz value that is at least an 2 magnitude smaller than the "granularity" of the numbers involved, and 4 orders of magnitude larger than the floating-point errors involved. In some cases, that can't be done, and you have to use "convert-to-integers". But In the case of my temp conversion programs, a suitable fuzz value was very easy to find, because the granularity is known.
(So why was I even asking about this here, if I already know these two methods? Simple: I was wondering if anyone knew any *OTHER* methods I hadn't considered.)
> with no understanding of what the real issue still is!
I understand the problem, and the "use integers" solution which others here have recommended to me. I just chose to avoid the hassle of that approach for these simple programs. The "integers" approach does have downsides: code bloat, complexity, unclarity, fragility, unmaintainability.
Oh, and you *STILL* have to use a "fuzz zone", to avoid converting to the next lower integer. Think about it! If you use an "expand by 1000" technique, you want 0.1999999999 to expand to 200, not 199. So you have to fuzz it by adding 0.01 to all your expansions after multiplying by 1000 but before casting to int. Else you're right back to "Equality failure of <= operator".
(Why 0.01? Two orders of magnitude below 1, and about four orders of magnitude above the x1000 expanded floating-point errors.)
If I used the "convert-to-integers" method for my temp-conversion programs, they would have to look something like the folowing (untested) code:
========= BEGIN "CONVERT-TO-INTEGERS" METHOD ======================
======== END "FUZZ-ZONE" METHOD =======================
*Much* simpler and more efficient, due to no conversions. Much more clear and understandable. Much easier to maintain.
The appropriate fuzz value of 0.00001 ensures that "<=" always does what we want it to, thus achieving everything that the "integers" method does, without all the bloat and confusion.
> What is the true value of CelsInc? Hint - it is not numerical value > 0.2 that you think it is.
Apparently it's not exactly 0.2, though I had thought it would be, because it's a terminating decimal.
I'm quite aware that at the realm of about 1 part in 1E10, there are errors in the internal representations of floating point numbers. There's only a finite number of bits, after all, and many real numbers need an infinite number of bits to exactly represent them: 1/3 = 0.3333333333...(to infinity) (can't be represented exactly) pi = 3.1415926535...(to infinity) (can't be represented exactly)
But as for 0.2, I'm not sure why it can't be exactly represented. Perhaps you can enlighten?
-- Cheers, Robbie Hatley lonewolf at well dot com www dot well dot com slant tilde lonewolf slant
Not sure, however, just how much of that is in the number itself, and to what extent it's due to printf()'s limitations. But one thing is clear: if *ANY* of those non-zero digits to the right of "0.2000000000000000" are actually being stored in the variable "zero_point_two", then it will cause exactly the bug I was having. And clearly my fuzz value of 0.00001 is perfect: far below granularity of 0.001, but far above error value 0.000000000000000011102230...
> The best pieces of advice you've gotten so far are:
> Control your loop with an integer, not a floating-point variable.
Considered, rejected. Fuzz-value method is better and simpler for this application.
> Read "What Every Computer Scientist Should Know About > Floating-Point Arithmetic" by David Goldberg.
Thanks for the reference, I'll try to hunt that down.
-- Cheers, Robbie Hatley lonewolf at well dot com www dot well dot com slant tilde lonewolf slant
So printf() doesn't print random gibberish at any point; it either print's what's there, or it prints 0 if nothing is there.
Hence, the actual ammount of error in the double-precision floating-point representation of 0.2 on my system & OS & compiler is 0.000000000000000011102230246251565404236
> Not sure, however, just how much of that is in the number itself, > and to what extent it's due to printf()'s limitations. But one > thing is clear: if *ANY* of those non-zero digits to the right > of "0.2000000000000000" are actually being stored in the variable > "zero_point_two", then it will cause exactly the bug I was > having. And clearly my fuzz value of 0.00001 is perfect: > far below granularity of 0.001, but far above error value > 0.000000000000000011102230...
> > The best pieces of advice you've gotten so far are:
> > Control your loop with an integer, not a floating-point variable.
> Considered, rejected. Fuzz-value method is better and simpler > for this application.
> > Read "What Every Computer Scientist Should Know About > > Floating-Point Arithmetic" by David Goldberg.
> Thanks for the reference, I'll try to hunt that down.
> -- > Cheers, > Robbie Hatley > lonewolf at well dot com > www dot well dot com slant tilde lonewolf slant
> So printf() doesn't print random gibberish at any point; > it either print's what's there, or it prints 0 if nothing > is there.
Actually, it looks like your system's printf gets bored at around 40 digits and just starts printing zeros instead of calculating any further. If it printed the exact value stored then you would have seen:
> So printf() doesn't print random gibberish at any point; > it either print's what's there, or it prints 0 if nothing > is there.
It's more complex than that. If you have a suitable version of printf (i.e. one that support C99 formats) you can see what is actually "there" by using the %a format. It takes a while to work out what the output means (it is a floating point number in hexadecimal) but it does show you what is really stored in a double.
> But as for 0.2, I'm not sure why it can't be exactly represented. > Perhaps you can enlighten?
Because binary floating point numbers can only exactly represent fractions where the denominator is a power of 1/2. In particular, this means that 0.2 = 1/5 cannot be exactly represented, in much the same way that 1/3 cannot be represented as a decimal floating point number.
>> > The <= issue is also now fixed. I used a +0.0001 "fuzz zone".
>> It is not fixed.
>Oh, but it *is* fixed.
>> You have simply camouflaged it.
>No, I "fuzzed" it. Read on.
>> You have managed to obtain the output you want
>Precisely. By adding a "fuzz zone" of 0.00001 in a program >where the numbers are increasing in increments of at least >0.001 (100 times larger), I've "fuzzed-out" (or "overloaded", >in C++ lingo) the meaning of operator "<=" to mean >"less-than OR approximately-equal-to-give-or-take-0.00001", >which cures the problem in the most-direct and simplist way.
Only by the unfortunate accident that all the values in question had sufficiently few significant digits. Since a double is guaranteed to have at least 10 significant digits, 40. + 0.00001 will always be greater than 40. But on a system where float has only 6 significant digits, 40.f + 0.00001f will be equal to 40.f. In this situation, your "fuzz zone" will do nothing.
>This "fuzz zone" approach can work well with much less code bloat >than the "convert-to-integers" method, PROVIDED that you can find >a fuzz value that is at least an 2 magnitude smaller than the >"granularity" of the numbers involved, and 4 orders of magnitude >larger than the floating-point errors involved. In some cases, >that can't be done, and you have to use "convert-to-integers". >But In the case of my temp conversion programs, a suitable >fuzz value was very easy to find, because the granularity is >known.
>(So why was I even asking about this here, if I already know >these two methods? Simple: I was wondering if anyone knew >any *OTHER* methods I hadn't considered.)
>> with no understanding of what the real issue still is!
>I understand the problem
Not if you believe what you wrote above. My system has built in decimal floating point and I don't need the fuzz at all if the fraction does not exceed the number of significant digits a double can hold.
snip
>But as for 0.2, I'm not sure why it can't be exactly represented. >Perhaps you can enlighten?
One procedure for converting a decimal fraction to binary is to multiply by two, use the integer portion, and repeat with the fractional portion, stopping when the fraction is 0
For example, 0.375 * 2 = 0.75; keep the 0. 0.75 * 2 = 1.5; keep the 1. 0.5 * 2 = 1.0; keep the 1 and stop. The binary representation of 0.375 is 0.011.
Now try it with 0.2 and let me know when the process terminates.