IComparer for floats? Heavy Math

  • Thread starter Thread starter Tom
  • Start date Start date
T

Tom

Has anyone ever seen a IComparer for floats the returns magnitude.
i.e. instead of returning -1, it would return -5. To let you know HOW
different the two numbers are. obviously for int it is a - b. But for
float the results would have to be normalize some how to a 32bit
range. I understand there would be percision errors.

Thanks
Tom
 
Tom,
You could use System.Math.Round, System.Math.Floor, or System.Math.Ceiling
to create your own...

Hope this helps
Jay
 
Tom said:
That would just give me 0 in most cases.

Round(.0-.5)=0
Round(1.5-1)=0

So you need to use Math.Floor for values < 0 and Math.Ceil for values >
0. That's not terribly tricky. You can still write your own reasonably
easily. The tricky bit is seeing whether the difference would overflow
or not. One way of doing that would be to halve both values, and if the
difference between *those* is greater than Float.MaxValue/2 or less
than -Float.MaxValue/2, then return Float.MaxValue or -Float.MaxValue.
Otherwise, return the difference.

I'd actually suggest that to keep things simple, you first check for
equality, and then work out which is greater. Then do the rest of the
calculations on the positive difference, and negate the result
appropriately at the end. That way you can deal with *just* Math.Ceil,
+Float.MaxValue etc.
 
Tom,
In addition to Jon's comments.

What are you expecting from comparing .0 with .5 & 1.5 with 1? Or even .5
with -.5? If you define what you are expecting then we can recommend the
correct

Also are you wanting a Magitude function or a CompareTo, as Magitude is not
really the purpose of the CompareTo function...

Hope this helps
Jay

Tom said:
That would just give me 0 in most cases.

Round(.0-.5)=0
Round(1.5-1)=0

"Jay B. Harlow [MVP - Outlook]" <[email protected]> wrote in message
Tom,
You could use System.Math.Round, System.Math.Floor, or System.Math.Ceiling
to create your own...

Hope this helps
Jay
 
I want the Magnitude of the difference. Say I have a series of floats

0.1 0.2 0.3 0.4 0.5 0.6

If I floor all these values they are 0.0 and all equal which is
obviously incorrect.

If I ceil all these values they are 1.0 and all equal which is also
obviously incorrect.

For fun lets also add the values.
1e+10 1e-10


now I compare .1 and .2 i with a normal compare I get 1
if I compare .1 and .3 I get 1
if I compare .1 and .5 I get 1
if I compare .1 and 1e+10 I get 1
if I compare .1 and 1e-10 I get -1

What I need to be able to do is say take 0.1 and 0.4 and 0.6
and say 0.4 is closer to 0.1 than 0.6

Obviously with just a series of floats this is trivial, the problem is
I am trying to accomplish this with a General Interface so I can apply
it to many types including classes, like ICompareable, which is how I
thought ICompareable was supposed to work. I didn't realize that -1 0
1 were the ONLY values that it return, even for ints.

Therefore I need to write a new interface that is similar to
IComparer, that will return the Magnitude of the differece. For ints
this is easy it's a-b. But for floats this is more difficult. How can
I write a function that will return a int representing the Magnitude
of the difference of floats, that will work for 1.0 and 1.1 but also
1e-10 and 1e+10 . i.e for 1.0 and 1.1 I may get a result of 5 but for
1e-10 and 1e+10 I may get a result of 500. The number itself isn't
important as long as it represents the magnitude of the diference up
to a certian percision. Possibly the int represents some sort of
logarythimic scale of the magnitude.

One way to solve this is to just have the interface return a float or
even a decimal instead of an int, then it would work for every type,
which is probably the right way to do this. I was just wondering if
this is some existing Mathimatical way, to normalize this difference
into the int range so a standard Comparer interface could be used.












Jay B. Harlow said:
Tom,
In addition to Jon's comments.

What are you expecting from comparing .0 with .5 & 1.5 with 1? Or even .5
with -.5? If you define what you are expecting then we can recommend the
correct

Also are you wanting a Magitude function or a CompareTo, as Magitude is not
really the purpose of the CompareTo function...

Hope this helps
Jay

Tom said:
That would just give me 0 in most cases.

Round(.0-.5)=0
Round(1.5-1)=0

"Jay B. Harlow [MVP - Outlook]" <[email protected]> wrote in message
Tom,
You could use System.Math.Round, System.Math.Floor, or System.Math.Ceiling
to create your own...

Hope this helps
Jay

Has anyone ever seen a IComparer for floats the returns magnitude.
i.e. instead of returning -1, it would return -5. To let you know HOW
different the two numbers are. obviously for int it is a - b. But for
float the results would have to be normalize some how to a 32bit
range. I understand there would be percision errors.

Thanks
Tom
 
Tom,
I don't see that your Magnitude function can return an Int.

How does that saying go: You can't put a square peg in a round hole. (You
cannot preserve the fraction in an Int value...).
One way to solve this is to just have the interface return a float or
even a decimal instead of an int, then it would work for every type,
which is probably the right way to do this. I was just wondering if
this is some existing Mathimatical way, to normalize this difference
into the int range so a standard Comparer interface could be used.
I would probably have it return a Decimal, although Single or Double may
also work:

http://www.yoda.arachsys.com/csharp/floatingpoint.html
http://www.yoda.arachsys.com/csharp/decimal.html

I suppose you could "normalize" the floats into Int64 values if there are
"significant" fractional parts. (if there is no integer difference, but
there is a fractional difference). However this may cause 11 with 21 to
return 10, while .011 and .021 also returns 10. Or you could "normalize" all
floats so that 11 & 21 return 10000, while .011 & .021 return 10.

Returning a Decimal, Single, Double or even a Quantity
(http://www.martinfowler.com/ap2/quantity.html) would allow you to return
the "Scale" of the difference along with the difference.

Hope this helps
Jay

Tom said:
I want the Magnitude of the difference. Say I have a series of floats

0.1 0.2 0.3 0.4 0.5 0.6

If I floor all these values they are 0.0 and all equal which is
obviously incorrect.

If I ceil all these values they are 1.0 and all equal which is also
obviously incorrect.

For fun lets also add the values.
1e+10 1e-10


now I compare .1 and .2 i with a normal compare I get 1
if I compare .1 and .3 I get 1
if I compare .1 and .5 I get 1
if I compare .1 and 1e+10 I get 1
if I compare .1 and 1e-10 I get -1

What I need to be able to do is say take 0.1 and 0.4 and 0.6
and say 0.4 is closer to 0.1 than 0.6

Obviously with just a series of floats this is trivial, the problem is
I am trying to accomplish this with a General Interface so I can apply
it to many types including classes, like ICompareable, which is how I
thought ICompareable was supposed to work. I didn't realize that -1 0
1 were the ONLY values that it return, even for ints.

Therefore I need to write a new interface that is similar to
IComparer, that will return the Magnitude of the differece. For ints
this is easy it's a-b. But for floats this is more difficult. How can
I write a function that will return a int representing the Magnitude
of the difference of floats, that will work for 1.0 and 1.1 but also
1e-10 and 1e+10 . i.e for 1.0 and 1.1 I may get a result of 5 but for
1e-10 and 1e+10 I may get a result of 500. The number itself isn't
important as long as it represents the magnitude of the diference up
to a certian percision. Possibly the int represents some sort of
logarythimic scale of the magnitude.

One way to solve this is to just have the interface return a float or
even a decimal instead of an int, then it would work for every type,
which is probably the right way to do this. I was just wondering if
this is some existing Mathimatical way, to normalize this difference
into the int range so a standard Comparer interface could be used.












"Jay B. Harlow [MVP - Outlook]" <[email protected]> wrote in message
Tom,
In addition to Jon's comments.

What are you expecting from comparing .0 with .5 & 1.5 with 1? Or even ..5
with -.5? If you define what you are expecting then we can recommend the
correct

Also are you wanting a Magitude function or a CompareTo, as Magitude is not
really the purpose of the CompareTo function...

Hope this helps
Jay

Tom said:
That would just give me 0 in most cases.

Round(.0-.5)=0
Round(1.5-1)=0

"Jay B. Harlow [MVP - Outlook]" <[email protected]> wrote in
message
Tom,
You could use System.Math.Round, System.Math.Floor, or System.Math.Ceiling
to create your own...

Hope this helps
Jay

Has anyone ever seen a IComparer for floats the returns magnitude.
i.e. instead of returning -1, it would return -5. To let you know HOW
different the two numbers are. obviously for int it is a - b. But for
float the results would have to be normalize some how to a 32bit
range. I understand there would be percision errors.

Thanks
Tom
 
Please explain for what do you need this? If you want to sort the values,
IComparer will be enough.
If you really want the magnitude of the difference, you have to state the
magnitude in double values,
you cannot express the difference of floating point number using integers.

--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
Tom said:
I want the Magnitude of the difference. Say I have a series of floats

0.1 0.2 0.3 0.4 0.5 0.6

If I floor all these values they are 0.0 and all equal which is
obviously incorrect.

If I ceil all these values they are 1.0 and all equal which is also
obviously incorrect.

For fun lets also add the values.
1e+10 1e-10


now I compare .1 and .2 i with a normal compare I get 1
if I compare .1 and .3 I get 1
if I compare .1 and .5 I get 1
if I compare .1 and 1e+10 I get 1
if I compare .1 and 1e-10 I get -1

What I need to be able to do is say take 0.1 and 0.4 and 0.6
and say 0.4 is closer to 0.1 than 0.6

Obviously with just a series of floats this is trivial, the problem is
I am trying to accomplish this with a General Interface so I can apply
it to many types including classes, like ICompareable, which is how I
thought ICompareable was supposed to work. I didn't realize that -1 0
1 were the ONLY values that it return, even for ints.

Therefore I need to write a new interface that is similar to
IComparer, that will return the Magnitude of the differece. For ints
this is easy it's a-b. But for floats this is more difficult. How can
I write a function that will return a int representing the Magnitude
of the difference of floats, that will work for 1.0 and 1.1 but also
1e-10 and 1e+10 . i.e for 1.0 and 1.1 I may get a result of 5 but for
1e-10 and 1e+10 I may get a result of 500. The number itself isn't
important as long as it represents the magnitude of the diference up
to a certian percision. Possibly the int represents some sort of
logarythimic scale of the magnitude.

One way to solve this is to just have the interface return a float or
even a decimal instead of an int, then it would work for every type,
which is probably the right way to do this. I was just wondering if
this is some existing Mathimatical way, to normalize this difference
into the int range so a standard Comparer interface could be used.



"Jay B. Harlow [MVP - Outlook]" <[email protected]> wrote in message
Tom,
In addition to Jon's comments.

What are you expecting from comparing .0 with .5 & 1.5 with 1? Or even ..5
with -.5? If you define what you are expecting then we can recommend the
correct

Also are you wanting a Magitude function or a CompareTo, as Magitude is not
really the purpose of the CompareTo function...

Hope this helps
Jay

Tom said:
That would just give me 0 in most cases.

Round(.0-.5)=0
Round(1.5-1)=0

"Jay B. Harlow [MVP - Outlook]" <[email protected]> wrote in
message
Tom,
You could use System.Math.Round, System.Math.Floor, or System.Math.Ceiling
to create your own...

Hope this helps
Jay

Has anyone ever seen a IComparer for floats the returns magnitude.
i.e. instead of returning -1, it would return -5. To let you know HOW
different the two numbers are. obviously for int it is a - b. But for
float the results would have to be normalize some how to a 32bit
range. I understand there would be percision errors.

Thanks
Tom
 
Tom said:
I want the Magnitude of the difference. Say I have a series of floats

0.1 0.2 0.3 0.4 0.5 0.6

If I floor all these values they are 0.0 and all equal which is
obviously incorrect.

If I ceil all these values they are 1.0 and all equal which is also
obviously incorrect.

I don't think anyone suggested applying Floor or ceil to the values
themselves - you'd apply Ceiling to the *difference* between them.
For fun lets also add the values.
1e+10 1e-10

now I compare .1 and .2 i with a normal compare I get 1
if I compare .1 and .3 I get 1
if I compare .1 and .5 I get 1
if I compare .1 and 1e+10 I get 1
if I compare .1 and 1e-10 I get -1

What I need to be able to do is say take 0.1 and 0.4 and 0.6
and say 0.4 is closer to 0.1 than 0.6

Obviously with just a series of floats this is trivial, the problem is
I am trying to accomplish this with a General Interface so I can apply
it to many types including classes, like ICompareable, which is how I
thought ICompareable was supposed to work. I didn't realize that -1 0
1 were the ONLY values that it return, even for ints.

It depends on the implementation. There may be some implementations
which return other values; there's certainly nothing to stop you from
writing one which *does* return other values, as the interface only
specifies it in terms of 0, less than 0 and greater than 0.
Therefore I need to write a new interface that is similar to
IComparer, that will return the Magnitude of the differece. For ints
this is easy it's a-b. But for floats this is more difficult. How can
I write a function that will return a int representing the Magnitude
of the difference of floats, that will work for 1.0 and 1.1 but also
1e-10 and 1e+10 . i.e for 1.0 and 1.1 I may get a result of 5 but for
1e-10 and 1e+10 I may get a result of 500. The number itself isn't
important as long as it represents the magnitude of the diference up
to a certian percision. Possibly the int represents some sort of
logarythimic scale of the magnitude.

A log does indeed sound like the way to go. I'd multiply the natural
log of the difference by 3 million and convert the result into an int.
That will use virtually the whole range of int to cover the whole range
of possible differences, assuming my maths is right. You should also
have a separate check that the result is only zero if the two numbers
actually *are* the same though - it's possible that the log of the
difference will be unrepresentably small, but the numbers themselves
aren't equal. You also need to be careful that the difference doesn't
overflow to start with.

That should guarantee that if the result of Compare between x and y is
greater than the result of Compare between y and z (and both are
positive) then z is closer to y than y is closer to x. It won't
guarantee the other way round, of course.

Oh, stuff it: here's some sample code. (It's interesting how many more
little problems showed up when I started coding it...)

using System;
using System.Collections;

class Test
{
static void Main()
{
LogDoubleComparer comp = new LogDoubleComparer();
// Test an extreme case...
Console.WriteLine (comp.Compare(Double.MaxValue,
Double.MinValue));
// And some less extreme ones
Console.WriteLine (comp.Compare(0.1, 0.4));
Console.WriteLine (comp.Compare(0.4, 0.6));
}
}

class LogDoubleComparer : IComparer
{
public int Compare (object a, object b)
{
if (! (a is double && b is double))
{
throw new ArgumentException
("LogDoubleComparer can only compare doubles");
}
double x = (double)a;
double y = (double)b;

// If they're equal, return 0 now, so we can assume for
// the rest of the method that they're not equal.
if (x==y)
{
return 0;
}

if (double.IsNaN(x) || double.IsInfinity(x) ||
double.IsNaN(y) || double.IsInfinity(y))
{
throw new ArgumentException
("LogDoubleComparer can't cope with NaN or infinity");
}

// Divide both by 2.5 to make absolutely sure we'll
// be able to calculate the difference without
// overflow

double c = x/2.5;
double d = y/2.5;

// Find the absolute value of the difference. We'll
// sort out the sign later.
double diff = Math.Abs(c-d);

// Add 2 to the difference to make sure the log is positive
// (We should now not be able to get a log result of 0,
// which is handy.)
diff += 2.0;

// log can now be NegativeInfinity (if diff is 0,
// or presumably if it's too close to zero to call)
// but shouldn't be NaN.
double log = Math.Log(diff);

// Now scale so that we get a wider range of values in int.
// We should still easily be within the range of int though.
log *= 3000000.0;

// Now convert to an int, taking the ceiling to make
// sure we don't actually return 0.
int ret = (int) Math.Ceiling(log);

// Now get the sign right - we need to invert if x < y
if (x < y)
{
ret = -ret;
}

return ret;
}
}
 
Back
Top