Reputation: 12894
Calling ToString()
on imprecise floats produces numbers a human would expect (eg. 4.999999... gets printed as 5
). However, when casting to int, the fractional part gets dropped and the result is an integer decremented by 1.
float num = 5f;
num *= 0.01f;
num *= 100f;
num.ToString().Dump(); // produces "5", as expected
((int)num).ToString().Dump(); // produces "4"
How do I cast the float to int, so that I get the human friendly value, that float.ToString()
produces?
I'm using this dirty hack:
int ToInt(float value) {
return (int)(value + Math.Sign(value) * 0.00001);
}
..but surely there must be a more elegant solution.
Edit: I'm well aware of the reasons why floats are truncated the way they are (4.999... to 4, etc.) The question is about casting to int
while emulating the default behavior of System.Single.ToString()
To better illustrate the behavior I'm looking for:
-4.99f should be cast to -4
4.01f should be cast to 4
4.99f should be cast to 4
4.999999f should be cast to 4
4.9999993f should be cast to 5
This is the exact same behavior that ToString
produces.
Try running this:
float almost5 = 4.9999993f;
Console.WriteLine(almost5); // "5"
Console.WriteLine((int)almost5); // "4"
Upvotes: 0
Views: 3137
Reputation: 12894
So far the best solution seems to be the one from the question.
int ToInt(float value) {
return (int)(value + Math.Sign(value) * 0.000001f);
}
This effectively snaps the value to the closest int, if the difference is small enough (less than 0.000001
). However, this function differs from ToString's behavior and is slightly more tolerant to imprecisions.
Another solution, suggested by @chux is to use ToString and parse the string back. Using Int32.Parse throws an exception when a number has a decimal point (or a comma), so you have to keep only the integer part of the string and it may cause other troubles depending on your default CultureInfo
.
Upvotes: 0
Reputation: 10849
0.05f * 100
is not exactly 5 due to floating point rounding (it's actually the value 4.999998.... when expressed as a float
The answer is that in the case of (int)(.05f * 100)
, you are taking the float value 4.999998 and truncating it to an integer, which yields 4
.
So use Math.Round
. The Math.Round function rounds a float value to the nearest integer, and rounds midpoint values to the nearest even number.
float num = 5f;
num *= 0.01f;
num *= 100f;
Console.WriteLine(num.ToString());
Console.WriteLine(((int)Math.Round(num)).ToString());
Upvotes: 1