|
P: n/a
|
Daniel
I am grappling with the idea of double.Epsilon. I have written the
following test:
[Test]
public void FuzzyDivisionTest()
{
double a = 0.33333d;
double b = 1d / 3d;
Assert.IsFalse(a == b, "Built-in == operator should not be
fuzzy");
Assert.IsTrue( a-b < double.Epsilon);
Assert.IsTrue( Math.Abs(a-b) < double.Epsilon);
Assert.IsTrue( Math.Abs(b-a) < double.Epsilon);
}
However, something is weird. The first 2 assertions pass, however the
last 2 fail. Is Math.Abs() losing my Epsilon value? Is that correct? | |
Share this Question
|
P: n/a
|
Jon Skeet [C# MVP]
Daniel <dhchait@yahoo.com> wrote:[color=blue]
> I am grappling with the idea of double.Epsilon. I have written the
> following test:
>
> [Test]
> public void FuzzyDivisionTest()
> {
> double a = 0.33333d;
> double b = 1d / 3d;
>
> Assert.IsFalse(a == b, "Built-in == operator should not be
> fuzzy");
> Assert.IsTrue( a-b < double.Epsilon);
> Assert.IsTrue( Math.Abs(a-b) < double.Epsilon);
> Assert.IsTrue( Math.Abs(b-a) < double.Epsilon);
> }
>
> However, something is weird. The first 2 assertions pass, however the
> last 2 fail. Is Math.Abs() losing my Epsilon value? Is that correct?[/color]
Epsilon is the smallest positive non-zero value that a double can hold.
It will never be true (I *think*) that a != b and
Math.Abs(b-a) < double.Epsilon.
a-b is less than double.Epsilon because it's negative.
--
Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
If replying to the group, please do not mail me too | | |
P: n/a
|
Jon
My interpretation of the Visual Studio help on this is that this reply is
partially correct. It is possible that
a != b
and
Math.Abs(b - a) < double.Epsilon
However the difference between a and b in your example is evidently larger
than double.Epsilon - it is 3.3333333(recurring)e-10, and double.Epsilon ==
4.94065645841247e-324
As the previous reply said, the reason the second assertion passes is that
a - b is negative.
"Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message
news:MPG.1ba7eb35102ad3eb98b35a@msnews.microsoft.c om...[color=blue]
> Daniel <dhchait@yahoo.com> wrote:[color=green]
> > I am grappling with the idea of double.Epsilon. I have written the
> > following test:
> >
> > [Test]
> > public void FuzzyDivisionTest()
> > {
> > double a = 0.33333d;
> > double b = 1d / 3d;
> >
> > Assert.IsFalse(a == b, "Built-in == operator should not be
> > fuzzy");
> > Assert.IsTrue( a-b < double.Epsilon);
> > Assert.IsTrue( Math.Abs(a-b) < double.Epsilon);
> > Assert.IsTrue( Math.Abs(b-a) < double.Epsilon);
> > }
> >
> > However, something is weird. The first 2 assertions pass, however the
> > last 2 fail. Is Math.Abs() losing my Epsilon value? Is that correct?[/color]
>
> Epsilon is the smallest positive non-zero value that a double can hold.
> It will never be true (I *think*) that a != b and
> Math.Abs(b-a) < double.Epsilon.
>
> a-b is less than double.Epsilon because it's negative.
>
> --
> Jon Skeet - <skeet@pobox.com>
> http://www.pobox.com/~skeet
> If replying to the group, please do not mail me too[/color] | | |
P: n/a
|
Niki Estner
"Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in
news:MPG.1ba7eb35102ad3eb98b35a@msnews.microsoft.c om...[color=blue]
> Daniel <dhchait@yahoo.com> wrote:[color=green]
>> I am grappling with the idea of double.Epsilon. I have written the
>> following test:
>>
>> [Test]
>> public void FuzzyDivisionTest()
>> {
>> double a = 0.33333d;
>> double b = 1d / 3d;
>>
>> Assert.IsFalse(a == b, "Built-in == operator should not be
>> fuzzy");
>> Assert.IsTrue( a-b < double.Epsilon);
>> Assert.IsTrue( Math.Abs(a-b) < double.Epsilon);
>> Assert.IsTrue( Math.Abs(b-a) < double.Epsilon);
>> }
>>
>> However, something is weird. The first 2 assertions pass, however the
>> last 2 fail. Is Math.Abs() losing my Epsilon value? Is that correct?[/color]
>
> Epsilon is the smallest positive non-zero value that a double can hold.
> It will never be true (I *think*) that a != b and
> Math.Abs(b-a) < double.Epsilon.[/color]
Yes, I think the docs about Double.Epsilon are actually wrong; Quote:
"Instead [of comparing double values with ==], determine if the two sides of
a comparison are close enough to equal for your purposes by comparing
whether the absolute value of the difference between the left and right-hand
sides is less than Epsilon."
I usually use something like "Math.Abs(a-b) < 1e-10", but I'm not sure if
this is common practice.
Niki | | |
P: n/a
|
Jon Skeet [C# MVP]
Jon <nospam@nospamever.com> wrote:[color=blue]
> My interpretation of the Visual Studio help on this is that this reply is
> partially correct. It is possible that
> a != b
> and
> Math.Abs(b - a) < double.Epsilon[/color]
Could you give an example? I believe that
Math.Abs(b-a) < double.Epsilon => b-a != 0 => b==a
It's the last step that I'm not sure about though - is it possible for
a and b to be different, but their difference to be so close to 0 as to
be unrepresentably small? It feels unlikely, but I know that intuition
is often horribly flawed when it comes to floating point maths.
--
Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
If replying to the group, please do not mail me too | | |
P: n/a
|
Jon Skeet [C# MVP]
Niki Estner <niki.estner@cube.net> wrote:[color=blue][color=green]
> > Epsilon is the smallest positive non-zero value that a double can hold.
> > It will never be true (I *think*) that a != b and
> > Math.Abs(b-a) < double.Epsilon.[/color]
>
> Yes, I think the docs about Double.Epsilon are actually wrong; Quote:
> "Instead [of comparing double values with ==], determine if the two sides of
> a comparison are close enough to equal for your purposes by comparing
> whether the absolute value of the difference between the left and right-hand
> sides is less than Epsilon."
>
> I usually use something like "Math.Abs(a-b) < 1e-10", but I'm not sure if
> this is common practice.[/color]
Yup - that seems a much better idea. Do you want to report the problem
to MS, or shall I?
--
Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
If replying to the group, please do not mail me too | | |
P: n/a
|
Niki Estner
"Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in
news:MPG.1ba801913dac652298b35b@msnews.microsoft.c om...[color=blue]
> Jon <nospam@nospamever.com> wrote:[color=green]
>> My interpretation of the Visual Studio help on this is that this reply is
>> partially correct. It is possible that
>> a != b
>> and
>> Math.Abs(b - a) < double.Epsilon[/color]
>
> Could you give an example? I believe that
>
> Math.Abs(b-a) < double.Epsilon => b-a != 0 => b==a
>
> It's the last step that I'm not sure about though - is it possible for
> a and b to be different, but their difference to be so close to 0 as to
> be unrepresentably small? It feels unlikely, but I know that intuition
> is often horribly flawed when it comes to floating point maths.[/color]
I guess it's possible if a and b are stored in 80-bit FPU registers: b-a (80
bit) could still be different form 0 (using an 80-bit comparison), but the
difference would be smaller than double.Epsilon. Also Math.Abs(b-a) would
actually return 0 (if not inlined) as b-a would get casted to a (64-bit)
double value.
Sample code (compares (double.Epsilon/2) with 0):
using System;
public class MyClass
{
public static void Main()
{
double a = double.Parse("2");
Console.WriteLine(0 == (double.Epsilon/a));
Console.WriteLine((double.Epsilon/a-0) < double.Epsilon);
}
}
Niki | | |
P: n/a
|
Niki Estner
"Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in
news:MPG.1ba801b7ca273b7298b35c@msnews.microsoft.c om...[color=blue]
> Niki Estner <niki.estner@cube.net> wrote:[color=green][color=darkred]
>> > Epsilon is the smallest positive non-zero value that a double can hold.
>> > It will never be true (I *think*) that a != b and
>> > Math.Abs(b-a) < double.Epsilon.[/color]
>>
>> Yes, I think the docs about Double.Epsilon are actually wrong; Quote:
>> "Instead [of comparing double values with ==], determine if the two sides
>> of
>> a comparison are close enough to equal for your purposes by comparing
>> whether the absolute value of the difference between the left and
>> right-hand
>> sides is less than Epsilon."
>>
>> I usually use something like "Math.Abs(a-b) < 1e-10", but I'm not sure if
>> this is common practice.[/color]
>
> Yup - that seems a much better idea. Do you want to report the problem
> to MS, or shall I?[/color]
You're the MVP ;-)
Niki | | |
P: n/a
|
Jon Skeet [C# MVP]
Niki Estner <niki.estner@cube.net> wrote:[color=blue][color=green]
> > It's the last step that I'm not sure about though - is it possible for
> > a and b to be different, but their difference to be so close to 0 as to
> > be unrepresentably small? It feels unlikely, but I know that intuition
> > is often horribly flawed when it comes to floating point maths.[/color]
>
> I guess it's possible if a and b are stored in 80-bit FPU registers: b-a (80
> bit) could still be different form 0 (using an 80-bit comparison), but the
> difference would be smaller than double.Epsilon. Also Math.Abs(b-a) would
> actually return 0 (if not inlined) as b-a would get casted to a (64-bit)
> double value.[/color]
Aargh, yes. I knew there was a good reason to be hesitant :)
[color=blue]
> Sample code (compares (double.Epsilon/2) with 0):[/color]
<snip>
Good, thanks. Here's something interesting, based on your code:
using System;
public class MyClass
{
public static void Main()
{
double two = double.Parse("2");
double a = double.Epsilon/two;
double b = 0;
Console.WriteLine(a==b);
Console.WriteLine(Math.Abs(b-a) < double.Epsilon);
}
}
That prints out (on my box):
True
True
If you comment out the last line, however, it just prints out
False
Presumably the call to Math.Abs forces a and b to be "normal" 64-bit
values rather than just 80-bit registers, or something like that.
--
Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
If replying to the group, please do not mail me too | | |
P: n/a
|
Jon Skeet [C# MVP]
Niki Estner <niki.estner@cube.net> wrote:[color=blue][color=green]
> > Yup - that seems a much better idea. Do you want to report the problem
> > to MS, or shall I?[/color]
>
> You're the MVP ;-)[/color]
Righto. (Admittedly I didn't have any luck persuading them that
System.Decimal is a floating point type rather than a fixed point type
- although reporting it again with the online bug database for 2.0,
it's been accepted!)
--
Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
If replying to the group, please do not mail me too | | |
P: n/a
|
Niki Estner
"Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in
news:MPG.1ba815e2cc7c8ef698b35e@msnews.microsoft.c om...[color=blue]
> Niki Estner <niki.estner@cube.net> wrote:
> <snip>
> Good, thanks. Here's something interesting, based on your code:
>
> using System;
> public class MyClass
> {
> public static void Main()
> {
> double two = double.Parse("2");
> double a = double.Epsilon/two;
> double b = 0;
> Console.WriteLine(a==b);
> Console.WriteLine(Math.Abs(b-a) < double.Epsilon);
> }
> }
>
> That prints out (on my box):
> True
> True
>
> If you comment out the last line, however, it just prints out
> False
>
> Presumably the call to Math.Abs forces a and b to be "normal" 64-bit
> values rather than just 80-bit registers, or something like that.[/color]
Yes, I never really understood when variables get enregistered and when not.
And the fact that the rules change as soon as I attach a debugger doesn't
make it easier either...
Niki | | |
P: n/a
|
Jon Skeet [C# MVP]
Niki Estner <niki.estner@cube.net> wrote:[color=blue][color=green]
> > Presumably the call to Math.Abs forces a and b to be "normal" 64-bit
> > values rather than just 80-bit registers, or something like that.[/color]
>
> Yes, I never really understood when variables get enregistered and when not.
> And the fact that the rules change as soon as I attach a debugger doesn't
> make it easier either...[/color]
I'm glad I'm not the only one - your previous posts suggest that you
know far more about JIT optimisation than I'm ever likely to :)
--
Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
If replying to the group, please do not mail me too | | |
P: n/a
|
Niki Estner
"Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in
news:MPG.1ba82bde1dee7b1798b362@msnews.microsoft.c om...[color=blue]
> Niki Estner <niki.estner@cube.net> wrote:[color=green][color=darkred]
>> > Presumably the call to Math.Abs forces a and b to be "normal" 64-bit
>> > values rather than just 80-bit registers, or something like that.[/color]
>>
>> Yes, I never really understood when variables get enregistered and when
>> not.
>> And the fact that the rules change as soon as I attach a debugger doesn't
>> make it easier either...[/color]
>
> I'm glad I'm not the only one - your previous posts suggest that you
> know far more about JIT optimisation than I'm ever likely to :)[/color]
Thanks a lot :-)
Unfortunately I'm guessing blindly more often than I'd like to, too...
I think it would be a wise decision if MS would at least release a little
more documentation on the internals of the JIT: Many developers simply
assume it doesn't optimize at all, others try to "optimize" their code and
break real optimizations that way (register-variables are quite fragile) or
create unmaintainable code. At least a few rules of thumb (when will it
remove range checking? when will it inline a function? when will it
enregister a variable? etc.) would be very helpful here...
On the other hand, we probably wouldn't have any use for cordbg any more
then; Wouldn't that be a shame?
Niki | | |
P: n/a
|
Daniel
Jon - thanks for the reply, I was hoping to hear from you or John
Bentley, the recognized leaders on the internet in this area!
*** Sent via Developersdex http://www.developersdex.com ***
Don't just participate in USENET...get rewarded for it! | | |
P: n/a
|
cody
I tend to say that operators == result is undefined for floating point types
(except decimal).
You never know how variables are stored in memory. testing for equality for
a floating point variables never makes sense, even if you do:
float a = 1f;
float b = 1f;
if (a==b) { }
The Jitter may choose to place a and b in difference register types (32, 64
or 80 bits), so a==b may return true or false depending on the machine type,
framework version, debug or release settings or if a debugger is attached or
in which way the variables are used in code or wheather the method is
inlined or not.
So it would be great if the compiler would at least issue a warning if it
spots code which tests for equality with floating point values.
The only thing I could imagine is testing for NaN but in that case one
should use IsNaN() methods of struct Double and Single.
--
cody
Freeware Tools, Games and Humour http://www.deutronium.de.vu || http://www.deutronium.tk
"Jon Skeet [C# MVP]" <skeet@pobox.com> schrieb im Newsbeitrag
news:MPG.1ba815e2cc7c8ef698b35e@msnews.microsoft.c om...[color=blue]
> Niki Estner <niki.estner@cube.net> wrote:[color=green][color=darkred]
> > > It's the last step that I'm not sure about though - is it possible for
> > > a and b to be different, but their difference to be so close to 0 as[/color][/color][/color]
to[color=blue][color=green][color=darkred]
> > > be unrepresentably small? It feels unlikely, but I know that intuition
> > > is often horribly flawed when it comes to floating point maths.[/color]
> >
> > I guess it's possible if a and b are stored in 80-bit FPU registers: b-a[/color][/color]
(80[color=blue][color=green]
> > bit) could still be different form 0 (using an 80-bit comparison), but[/color][/color]
the[color=blue][color=green]
> > difference would be smaller than double.Epsilon. Also Math.Abs(b-a)[/color][/color]
would[color=blue][color=green]
> > actually return 0 (if not inlined) as b-a would get casted to a (64-bit)
> > double value.[/color]
>
> Aargh, yes. I knew there was a good reason to be hesitant :)
>[color=green]
> > Sample code (compares (double.Epsilon/2) with 0):[/color]
>
> <snip>
>
> Good, thanks. Here's something interesting, based on your code:
>
> using System;
> public class MyClass
> {
> public static void Main()
> {
> double two = double.Parse("2");
> double a = double.Epsilon/two;
> double b = 0;
> Console.WriteLine(a==b);
> Console.WriteLine(Math.Abs(b-a) < double.Epsilon);
> }
> }
>
> That prints out (on my box):
> True
> True
>
> If you comment out the last line, however, it just prints out
> False
>
> Presumably the call to Math.Abs forces a and b to be "normal" 64-bit
> values rather than just 80-bit registers, or something like that.
>
> --
> Jon Skeet - <skeet@pobox.com>
> http://www.pobox.com/~skeet
> If replying to the group, please do not mail me too[/color] | | |
P: n/a
|
cody
Damn. Errata:
NaN cannot be used to test for is-not-a-number anyway. You *have to* use
IsNaN instead.
The same is true for NegativeInfinity and PositiveInfinity.
--
cody
Freeware Tools, Games and Humour http://www.deutronium.de.vu || http://www.deutronium.tk
"cody" <no_spam_deutronium@gmx.net> schrieb im Newsbeitrag
news:#LxFkoXlEHA.3476@tk2msftngp13.phx.gbl...[color=blue]
> I tend to say that operators == result is undefined for floating point[/color]
types[color=blue]
> (except decimal).
> You never know how variables are stored in memory. testing for equality[/color]
for[color=blue]
> a floating point variables never makes sense, even if you do:
>
> float a = 1f;
> float b = 1f;
> if (a==b) { }
>
> The Jitter may choose to place a and b in difference register types (32,[/color]
64[color=blue]
> or 80 bits), so a==b may return true or false depending on the machine[/color]
type,[color=blue]
> framework version, debug or release settings or if a debugger is attached[/color]
or[color=blue]
> in which way the variables are used in code or wheather the method is
> inlined or not.
>
> So it would be great if the compiler would at least issue a warning if it
> spots code which tests for equality with floating point values.
>
> The only thing I could imagine is testing for NaN but in that case one
> should use IsNaN() methods of struct Double and Single.
>
> --
> cody
>
> Freeware Tools, Games and Humour
> http://www.deutronium.de.vu || http://www.deutronium.tk
> "Jon Skeet [C# MVP]" <skeet@pobox.com> schrieb im Newsbeitrag
> news:MPG.1ba815e2cc7c8ef698b35e@msnews.microsoft.c om...[color=green]
> > Niki Estner <niki.estner@cube.net> wrote:[color=darkred]
> > > > It's the last step that I'm not sure about though - is it possible[/color][/color][/color]
for[color=blue][color=green][color=darkred]
> > > > a and b to be different, but their difference to be so close to 0 as[/color][/color]
> to[color=green][color=darkred]
> > > > be unrepresentably small? It feels unlikely, but I know that[/color][/color][/color]
intuition[color=blue][color=green][color=darkred]
> > > > is often horribly flawed when it comes to floating point maths.
> > >
> > > I guess it's possible if a and b are stored in 80-bit FPU registers:[/color][/color][/color]
b-a[color=blue]
> (80[color=green][color=darkred]
> > > bit) could still be different form 0 (using an 80-bit comparison), but[/color][/color]
> the[color=green][color=darkred]
> > > difference would be smaller than double.Epsilon. Also Math.Abs(b-a)[/color][/color]
> would[color=green][color=darkred]
> > > actually return 0 (if not inlined) as b-a would get casted to a[/color][/color][/color]
(64-bit)[color=blue][color=green][color=darkred]
> > > double value.[/color]
> >
> > Aargh, yes. I knew there was a good reason to be hesitant :)
> >[color=darkred]
> > > Sample code (compares (double.Epsilon/2) with 0):[/color]
> >
> > <snip>
> >
> > Good, thanks. Here's something interesting, based on your code:
> >
> > using System;
> > public class MyClass
> > {
> > public static void Main()
> > {
> > double two = double.Parse("2");
> > double a = double.Epsilon/two;
> > double b = 0;
> > Console.WriteLine(a==b);
> > Console.WriteLine(Math.Abs(b-a) < double.Epsilon);
> > }
> > }
> >
> > That prints out (on my box):
> > True
> > True
> >
> > If you comment out the last line, however, it just prints out
> > False
> >
> > Presumably the call to Math.Abs forces a and b to be "normal" 64-bit
> > values rather than just 80-bit registers, or something like that.
> >
> > --
> > Jon Skeet - <skeet@pobox.com>
> > http://www.pobox.com/~skeet
> > If replying to the group, please do not mail me too[/color]
>
>[/color] | | |
P: n/a
|
Niki Estner
"cody" <no_spam_deutronium@gmx.net> schrieb im Newsbeitrag
news:%23LxFkoXlEHA.3476@tk2msftngp13.phx.gbl...[color=blue]
>I tend to say that operators == result is undefined for floating point
>types
> (except decimal).
> You never know how variables are stored in memory.[/color]
Of course you do. The *memory* size is determined by the variable's type.
[color=blue]
> testing for equality for
> a floating point variables never makes sense, even if you do:
>
> float a = 1f;
> float b = 1f;
> if (a==b) { }
>
> The Jitter may choose to place a and b in difference register types (32,
> 64
> or 80 bits), so a==b may return true or false depending on the machine
> type,
> framework version, debug or release settings or if a debugger is attached
> or
> in which way the variables are used in code or wheather the method is
> inlined or not.[/color]
No, I'm pretty sure (a==b) will always be true here: AFAIK it's guaranteed
that converting a 32-bit floating point value to 64 bit or 80 bit will never
cause any loss in precision; Also, you're guaranteed that a 64-bit value
will never be stored in a 32-bit or smaller register (note that intel FPU's
don't have 32-bit or 64-bit registers anyway). So, in the case of constants
the '==' operator should always work correctly.
[color=blue]
> So it would be great if the compiler would at least issue a warning if it
> spots code which tests for equality with floating point values.[/color]
It's common to test a floating point value for some constant it's
initialized to (like: if (a == 0) a = GetSomeValue();). Other tests for
common values (e.g. double.Parse(str) == 1) will work, too.
A warning wouldn't hurt (if it can be turned off), but I would prefer a
special "about equal" operator with fuzzy comparison rules.
Niki | | |
P: n/a
|
Jon Skeet [C# MVP]
Niki Estner <niki.estner@cube.net> wrote:[color=blue][color=green]
> > So it would be great if the compiler would at least issue a warning if it
> > spots code which tests for equality with floating point values.[/color]
>
> It's common to test a floating point value for some constant it's
> initialized to (like: if (a == 0) a = GetSomeValue();). Other tests for
> common values (e.g. double.Parse(str) == 1) will work, too.
> A warning wouldn't hurt (if it can be turned off), but I would prefer a
> special "about equal" operator with fuzzy comparison rules.[/color]
I don't think I'd want an operator, but a utility method on Double
wouldn't hurt. It could either take the two values and the "fuzziness
factor", or there could perhaps be a default fuzziness factor which
could be changed programatically.
--
Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
If replying to the group, please do not mail me too | | |
P: n/a
|
cody
> >I tend to say that operators == result is undefined for floating point[color=blue][color=green]
> >types
> > (except decimal).
> > You never know how variables are stored in memory.[/color]
>
> Of course you do. The *memory* size is determined by the variable's type.[/color]
Yes in memory, but you do not know which CPU registers will be used.
[color=blue]
> No, I'm pretty sure (a==b) will always be true here: AFAIK it's guaranteed
> that converting a 32-bit floating point value to 64 bit or 80 bit will[/color]
never[color=blue]
> cause any loss in precision;[/color]
Maybe not precision but it will not be exactly the same value.
The following program outputs "true true false false" on my machine:
double d=0.0;
float f=0.0f;
double d1=1.0;
float f1=1.0f;
double d2=1.3;
float f2=1.3f;
float f3=float.Parse("0.2");
float f4=0.2f;
MessageBox.Show(string.Format("{0} {1} {2} {3}", d==f, d1==f1, d2==f2,
f3==f4));
[color=blue]
> Also, you're guaranteed that a 64-bit value
> will never be stored in a 32-bit or smaller register[/color]
Thats clear. It it only guaranteed that a variable size 32 bit will be
stored in a register with *at least* 32 bit or more.
So, in the case of constants the '==' operator should always work
correctly.
It won't see above.
[color=blue]
> It's common to test a floating point value for some constant it's
> initialized to (like: if (a == 0) a = GetSomeValue();). Other tests for
> common values (e.g. double.Parse(str) == 1) will work, too.[/color]
If you are lucky it will work on some machines with some values.
[color=blue]
> A warning wouldn't hurt (if it can be turned off), but I would prefer a
> special "about equal" operator with fuzzy comparison rules.[/color]
I'd vote against it. Testing equality is always bad idea anyway. Even if,
there must be a possibility to specify the tolerance value because the
fuzzyness really depends on the application. The method wouldn't do anything
else but Math.Abs(a,b)<Tolerance so there is very little point for including
such a method in the framework.
--
cody
Freeware Tools, Games and Humour http://www.deutronium.de.vu || http://www.deutronium.tk | | |
P: n/a
|
Niki Estner
"cody" <no_spam_deutronium@gmx.net> wrote in
news:ucHoVCZlEHA.596@TK2MSFTNGP11.phx.gbl...[color=blue]
>...
> Maybe not precision but it will not be exactly the same value.
> The following program outputs "true true false false" on my machine:
>
> double d=0.0;
> float f=0.0f;
> double d1=1.0;
> float f1=1.0f;
> double d2=1.3;
> float f2=1.3f;
> float f3=float.Parse("0.2");
> float f4=0.2f;
> MessageBox.Show(string.Format("{0} {1} {2} {3}", d==f, d1==f1, d2==f2,
> f3==f4));
> ...[/color]
The point is that
float a=1.3f;
double b=1.3f;
Console.WriteLine(a==b);
Will always return true, because converting "1.3f" (or any other 32-bit
float-value) to double will not change it; Of course this value can actually
be different from "1.3d".
Your original example was:
float a = 1f;
float b = 1f;
if (a==b) { }
As shown above, "1f" (or any other float constant) will not be changed when
converted to a double (or 80-bit fp value), so that kind of comparison *is*
safe.
Niki | | |
P: n/a
|
Jeremy Davis
"cody" wrote:
[color=blue][color=green][color=darkred]
> > >I tend to say that operators == result is undefined for floating point
> > >types
> > > (except decimal).
> > > You never know how variables are stored in memory.[/color]
> >
> > Of course you do. The *memory* size is determined by the variable's type.[/color]
>
> Yes in memory, but you do not know which CPU registers will be used.
>[color=green]
> > No, I'm pretty sure (a==b) will always be true here: AFAIK it's guaranteed
> > that converting a 32-bit floating point value to 64 bit or 80 bit will[/color]
> never[color=green]
> > cause any loss in precision;[/color]
>
> Maybe not precision but it will not be exactly the same value.
> The following program outputs "true true false false" on my machine:
>
> double d=0.0;
> float f=0.0f;
> double d1=1.0;
> float f1=1.0f;
> double d2=1.3;
> float f2=1.3f;
> float f3=float.Parse("0.2");
> float f4=0.2f;
> MessageBox.Show(string.Format("{0} {1} {2} {3}", d==f, d1==f1, d2==f2,
> f3==f4));
>[color=green]
> > Also, you're guaranteed that a 64-bit value
> > will never be stored in a 32-bit or smaller register[/color]
>
> Thats clear. It it only guaranteed that a variable size 32 bit will be
> stored in a register with *at least* 32 bit or more.
>
> So, in the case of constants the '==' operator should always work
> correctly.
>
> It won't see above.
>[color=green]
> > It's common to test a floating point value for some constant it's
> > initialized to (like: if (a == 0) a = GetSomeValue();). Other tests for
> > common values (e.g. double.Parse(str) == 1) will work, too.[/color]
>
> If you are lucky it will work on some machines with some values.
>[color=green]
> > A warning wouldn't hurt (if it can be turned off), but I would prefer a
> > special "about equal" operator with fuzzy comparison rules.[/color]
>
> I'd vote against it. Testing equality is always bad idea anyway. Even if,
> there must be a possibility to specify the tolerance value because the
> fuzzyness really depends on the application. The method wouldn't do anything
> else but Math.Abs(a,b)<Tolerance so there is very little point for including
> such a method in the framework.
>
> --
> cody
>
> Freeware Tools, Games and Humour
> http://www.deutronium.de.vu || http://www.deutronium.tk[/color]
Equality comparison between integers (within the range and precision of your
chosen data type) and rational numbers of the form x/(2^y) ought to always
work. Other than that, you get into the ugliness of trying to losslessly
convert non-integral values between bases. (We all know how fun it is to
write out 1/3 as an "exact" decimal.) | | |
P: n/a
|
cody
> > Maybe not precision but it will not be exactly the same value.[color=blue][color=green]
> > The following program outputs "true true false false" on my machine:
> >
> > double d=0.0;
> > float f=0.0f;
> > double d1=1.0;
> > float f1=1.0f;
> > double d2=1.3;
> > float f2=1.3f;
> > float f3=float.Parse("0.2");
> > float f4=0.2f;
> > MessageBox.Show(string.Format("{0} {1} {2} {3}", d==f, d1==f1, d2==f2,
> > f3==f4));
> > ...[/color]
>
> The point is that
> float a=1.3f;
> double b=1.3f;
> Console.WriteLine(a==b);
> Will always return true, because converting "1.3f" (or any other 32-bit
> float-value) to double will not change it; Of course this value can[/color]
actually[color=blue]
> be different from "1.3d".[/color]
But the comparison returned FALSE! Did you read my posting?
[color=blue]
> Your original example was:
> float a = 1f;
> float b = 1f;
> if (a==b) { }
>
> As shown above, "1f" (or any other float constant) will not be changed[/color]
when[color=blue]
> converted to a double (or 80-bit fp value), so that kind of comparison[/color]
*is*[color=blue]
> safe.[/color]
This could cause the same problem as my first example. I can remember that
soem time ago someone gave me an example of values which really were
difference although there were both float and originally the same constant
was assigned to them.
--
cody
Freeware Tools, Games and Humour http://www.deutronium.de.vu || http://www.deutronium.tk | | |
P: n/a
|
Niki Estner
"cody" <no_spam_deutronium@gmx.net> wrote in
news:%23rsH1lblEHA.1356@TK2MSFTNGP09.phx.gbl...[color=blue]
> ...
> But the comparison returned FALSE! Did you read my posting?[/color]
Cody, please! Correct your code to:
double d=0.0f;
float f=0.0f;
double d1=1.0f;
float f1=1.0f;
double d2=1.3f;
float f2=1.3f;
float f3=float.Parse("0.2");
float f4=0.2f;
Because that's what we've been talking about: converting a 32-bit float to a
64-bit float won't change it's value.
So, it's always safe to compare a 32-bit float with another 32-bit float,
even if the JITer chooses to put it in a 80-bit FPU register.
[color=blue][color=green]
>> Your original example was:
>> float a = 1f;
>> float b = 1f;
>> if (a==b) { }
>>
>> As shown above, "1f" (or any other float constant) will not be changed[/color]
> when[color=green]
>> converted to a double (or 80-bit fp value), so that kind of comparison[/color]
> *is*[color=green]
>> safe.[/color]
>
> This could cause the same problem as my first example. I can remember that
> soem time ago someone gave me an example of values which really were
> difference although there were both float and originally the same constant
> was assigned to them.[/color]
So, why don't you show that example?
Look, a 32-bit FP number has 1 sign bit, 8 bits exponent and 23 mantissa
bits.
A 64-bit FP number has 1 sign bit, 11 bits for the exponent, and 52 bits for
the mantissa part.
If you convert a 32-bit fp to a 64-bit fp number, all you have to do is
"append zeros" to the mantissa part, which doesn't change it's value, and
convert the exponent from 8 to 11 bits (which doesn't change it either). So,
how do you think something like this
float f1 = SomeValue;
Console.Writeline(f1 == (double)f1);
could ever return false (unless SomeValue was NaN, of course)?
Niki | | |
P: n/a
|
cody
> > But the comparison returned FALSE! Did you read my posting?[color=blue]
>
> Cody, please! Correct your code to:
>[/color]
[..][color=blue]
> double d2=1.3f;
> float f2=1.3f;[/color]
[..]
Damn you are right.. A very subtle difference. Now I understand. 1.3 is not
equal to 1.3f, but 1.3f is always 1.3f, no matter if it is put into a float
or double variable or stored in a 64 or 80 bit register.
[color=blue]
> Look, a 32-bit FP number has 1 sign bit, 8 bits exponent and 23 mantissa
> bits.
> A 64-bit FP number has 1 sign bit, 11 bits for the exponent, and 52 bits[/color]
for[color=blue]
> the mantissa part.
> If you convert a 32-bit fp to a 64-bit fp number, all you have to do is
> "append zeros" to the mantissa part, which doesn't change it's value, and
> convert the exponent from 8 to 11 bits (which doesn't change it either).[/color]
Very interesting information.
But I could swear I read somewhere that the register will affect the
comparison :)
--
cody
[Freeware, Games and Humor] www.deutronium.de.vu || www.deutronium.tk | | |
P: n/a
|
Jon Skeet [C# MVP]
cody <please_dont.spam.deutronium@gmx.de> wrote:[color=blue][color=green]
> > If you convert a 32-bit fp to a 64-bit fp number, all you have to do is
> > "append zeros" to the mantissa part, which doesn't change it's value, and
> > convert the exponent from 8 to 11 bits (which doesn't change it either).[/color]
>
> Very interesting information.
>
> But I could swear I read somewhere that the register will affect the
> comparison :)[/color]
It can if the conversion goes the other way - if a register holds an 80
bit number and that's converted to a 64 bit number.
Here's an example which actually didn't show up the problem on your box
last time I posted it, but it does show it on mine:
using System;
class Test
{
static float member;
static void Main()
{
member = Calc();
float local = Calc();
Console.WriteLine(local==member);
}
static float Calc()
{
float d1 = 2.82323f;
float d2 = 2.3f;
return d1*d2;
}
}
The member variable is strictly 32 bits, but the local variable can be
enregistered - so it ends up being more accurate than the member
variable, and the comparison fails (on my box).
--
Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
If replying to the group, please do not mail me too | | Post your reply Help answer this question
Didn't find the answer to your C# / C Sharp question?
| | Question stats - viewed: 6941
- replies: 44
- date asked: Nov 16 '05
|