double or float?

  • Thread starter Thread starter James Thurley
  • Start date Start date
J

James Thurley

According to the docs, floats are 32 bit and doubles are 64 bit. So
using floats should be faster than using doubles on a 32 bit processor,
and my tests confirm this. However, most of the Math methods deal with
doubles and I'm having problems with casting them back to floats.

For example:
(double)0.1f = 0.10000000149011612

I don't need the accuracy of doubles, but the speed of floats would be
benificial, but it seems I'm either going to have to write my own math
functions or just use doubles. For the record my rather basic test,
which just performed lots of multiplications and divisions, took half
the time using floats as using doubles.

Has anyone else had this problem? And can someone give me an efficient
Floor algorithm so I can write my own floating point version?

James.
 
Michael said:
I guess I'm curious why you can't cast back to floats? Do you need the
precision or not? I have a feeling you're running into this problem:

http://www.pobox.com/~skeet/csharp/floatingpoint.html
(thanks to Jon Skeet for the article)

I don't need double precision, but the inaccuracy caused by casting can
make, for example, the Floor function give the incorrect answer:

float min=1.0f, increment=0.1f;
double result = Math.Floor(min/increment)*increment;

I'm leaving the result as a double in this example - its casting from
float to double that's causing the problem here. In theory,
min/increment will be 10.0 which is also the Floor. Multiply it by
increment and you get 1.0 again. But result comes out as
0.90000001341104507. (Try it - I swear it's true!)

Now I get that number if I put "Math.Floor(1.0f/(double)0.1f)*0.1f" into
the watch window. Even wierder, If I copy and paste
"Math.Floor(min/increment)*increment" into the watch window it gives me
the correct answer of 1.0. So I guess the compiler must be making the
conversion to double earlier than I would have expected, but either way
it's causing a problem.

I'm starting to think I should just use doubles... processors of the
future will be 64bit anyway I guess.
 
The code at the end demonstrates your problem. When casting from a float
number up to a double, you are getting some slightly skewed results. If you
divide 1/.1 in float or double you always get 10, however, casting a float
10 to a double results in some small amount of inaccuracy 9.99999985098839.
Generally you can work this off by using an error quotient or some small
decimal that gives a degree of freedom. This could be demonstrated by doing
(float fixedError = .0001f; Math.Floor((min/increment)+fixedError);) You
could also make use of the Round function instead of the floor method, or
you could roll your own floor method for floats.

using System;

public class Locality {
private static void Main(string[] args) {
float min=1.0f, increment=0.1f;
double mind=1.0D, incrementd=0.1D;

Console.WriteLine(min/increment);
Console.WriteLine(mind/incrementd);
Console.WriteLine((double) (min/increment));

Console.WriteLine(Math.Floor(min/increment));
Console.WriteLine(Math.Floor(mind/incrementd));

double result = Math.Floor(min/increment)*increment;
Console.WriteLine(result);
}
}
 
I don't need double precision, but the inaccuracy caused by casting can
make, for example, the Floor function give the incorrect answer:

float min=1.0f, increment=0.1f;
double result = Math.Floor(min/increment)*increment;

There is no exact representation of 0.1 in binary.
 
Justin said:
The code at the end demonstrates your problem. When casting from a float
number up to a double, you are getting some slightly skewed results. If you
divide 1/.1 in float or double you always get 10, however, casting a float
10 to a double results in some small amount of inaccuracy 9.99999985098839.
Generally you can work this off by using an error quotient or some small
decimal that gives a degree of freedom. This could be demonstrated by doing
(float fixedError = .0001f; Math.Floor((min/increment)+fixedError);) You
could also make use of the Round function instead of the floor method, or
you could roll your own floor method for floats.

using System;

public class Locality {
private static void Main(string[] args) {
float min=1.0f, increment=0.1f;
double mind=1.0D, incrementd=0.1D;

Console.WriteLine(min/increment);
Console.WriteLine(mind/incrementd);
Console.WriteLine((double) (min/increment));

Console.WriteLine(Math.Floor(min/increment));
Console.WriteLine(Math.Floor(mind/incrementd));

double result = Math.Floor(min/increment)*increment;
Console.WriteLine(result);
}
}

Good idea - for now I'll do Math.Floor(Math.Round(min/increment,6))
which solves the problem. I'll have to write my own Floor function to
make it slick again. Can't for the life of me think of an efficient
algorithm though...
 
A floating point version of floor? Remember floor is doing nothing more
than storing an integer value so the (int) cast is perfect for this
operation. Computing the ceiling is a bit harder, but since you don't need
that I won't worry myself. It also works for doubles. Note that
up-conversions are implicit so casting to int, then straight back up to
float all happens in the assignment.

float fresult = min/(increment);
float floor = (int) fresult;

--
Justin Rogers
DigiTec Web Consultants, LLC.


James Thurley said:
Justin said:
The code at the end demonstrates your problem. When casting from a float
number up to a double, you are getting some slightly skewed results. If you
divide 1/.1 in float or double you always get 10, however, casting a float
10 to a double results in some small amount of inaccuracy 9.99999985098839.
Generally you can work this off by using an error quotient or some small
decimal that gives a degree of freedom. This could be demonstrated by doing
(float fixedError = .0001f; Math.Floor((min/increment)+fixedError);) You
could also make use of the Round function instead of the floor method, or
you could roll your own floor method for floats.

using System;

public class Locality {
private static void Main(string[] args) {
float min=1.0f, increment=0.1f;
double mind=1.0D, incrementd=0.1D;

Console.WriteLine(min/increment);
Console.WriteLine(mind/incrementd);
Console.WriteLine((double) (min/increment));

Console.WriteLine(Math.Floor(min/increment));
Console.WriteLine(Math.Floor(mind/incrementd));

double result = Math.Floor(min/increment)*increment;
Console.WriteLine(result);
}
}

Good idea - for now I'll do Math.Floor(Math.Round(min/increment,6))
which solves the problem. I'll have to write my own Floor function to
make it slick again. Can't for the life of me think of an efficient
algorithm though...
 
Justin Rogers said:
A floating point version of floor? Remember floor is doing nothing more
than storing an integer value so the (int) cast is perfect for this
operation.

Except they do different things for negative numbers - casting to int
rounds to 0, Floor always rounds down.
 
Jon said:
Except they do different things for negative numbers - casting to int
rounds to 0, Floor always rounds down.

Hmm... I guess something like this should work:

public int Floor(float val){
if(val>0.0f || val==(int)val)
return (int)val; // Positive numbers and negative whole numbers
else
return ((int)val) - 1; // Negative floating point numbers
}

Not very pretty, but should be fast!
 
I'm starting to think I should just use doubles... processors of the
future will be 64bit anyway I guess.

the "bitness" of a CPU has nothing to do with this.
On a 32 bit CPU a float can be fetched in 1 cycle and a double in 2 cycles
On a 64 bit CPU 2 floats can be fetched in 1 cycle and a double in 1 cycle

So the net effect will be the same.
The only thing that is really a factor is how well the FP units within the
CPU are implemented.

If you are really trying to crunch numbers then look at the CPU specific
SIMD instructions available for your target CPU. Oh, and forget c# since
the runtime doesn't use them.

Oscar
 
Back
Top