How to get the length of a Float? (C#) - c#

I'm learning C# from the 'Fundamentals of Computer Programming with C#' by Svetlin Nakov and others (available for free here: http://www.introprogramming.info/english-intro-csharp-book/)
After each chapter, the authors like to ask questions that go beyond the scope of the chapter. Pg 135, Question 3 asks me to write a program that can correctly compare two real numbers with an accuracy of 0.000001 (7 significant digits).
So I'm using floats to compare the numbers and I decided to add some code that would check to see if the numbers entered are longer than the 7 significant digits that floats can handle. So I need to check for the number of significant digits. Google tells me that I should use sizeof(float) to do that, but I keep getting CS0246 error on the lines of the sizeof check (The type or namespace could not be found.)
The program works if I don't include the code that checks the length of the numbers. I can't find a answer on SO for C#.
What's the problem?
Edit: Thanks for all the answers. Let me clarify my question: I understand that parsing for string to float automatically checks for validity. However, I tried with my program yesterday, and floats will lose any more than 7 significant digits. So if I compare 0.123457 and 0.12345678, the program will declare that the two numbers are the same because the second number is rounded up. That's why I'm trying to catch for floats longer than 7 digits. I interpret the question this way because it occured to me that these two very similar, but not identical numbers slip through the cracks.
using System;
// Compare two real numbers with up to 0.000001 (7) significant digits
class Compare_Numbers
{
static void Main(string[] args)
{
// Processing the first number
String firstNumString = null;
Console.WriteLine("This program compares 2 numbers with upto 7 significant digits.\nEnter the FIRST number with up to 7 significant digits");
firstNumString = Console.ReadLine();
float firstNum = Single.Parse(firstNumString);
if (sizeof(firstNum) > 7)
{
Console.WriteLine("That number is too long!\nEnter a number with a MAX of 7 significant digits!");
}
// Processing the second number
String secondNumString = null;
Console.WriteLine("Enter the SECOND number with up to 7 significant digits");
secondNumString = Console.ReadLine();
float secondNum = Single.Parse(secondNumString);
if (sizeof(secondNum) > 7)
{
Console.WriteLine("That number is too long!\nEnter a number with a MAX of 7 significant digits!");
}
if (firstNum == secondNum)
{
Console.WriteLine("The two numbers are the SAME!");
}
else
{
Console.WriteLine("The two numbers are DIFFERENT!");
}
}
}

static private int GetNumDigitsInFloat(float n)
{
string s = n.ToString();
return s.Length - 1;
}

"How to get the length of a Float?"
In short, assuming 7 significant digits:
firstNum.ToString("G7").Length // 7 significant digits
Ex.
float pi = 3.14159265f;
string g5 = a.ToString("G5");
string g7 = a.ToString("G7");
However, your title asks something simple, but the body of your question indicates something else. So it appears that you think finding the length of a float is en route to a larger solution. I am not sure, so I will just try to point out several issues.
First, you are misusing sizeof; sizeof() in C# takes a type, not a variable. (So sizeof(float) would work, but not sizeof(num) ).
In any case, sizeof it isn't going to give you the number of significant digits. It will give you the number of bytes for the storage of the unmanaged type, which will be constant (4, 8, etc.). Instead, for a given string, use String.Length
However, what you can't do is try to parse the number to a float, and then try to check for out of range values by checking the float variable. By definition, if you can successfully parse to a float, then it was valid. If it is invalid, it won't parse. The part of your example where you use Single.Parse() and then proceed to try validating using the float variable is moot. You need to validate the string value, or validate that the parse succeeds, or change your approach.
I think the simplest solution is to just use Single.TryParse() and check the boolean return value. if it returns false, either the string value is invalid, or out of range of Single.MinValue and Single.MaxValue. So you might rethink your approach (since it isn't the author's original challenge). I, personally would use the large type in C# for my calculator, but the purpose of the exercise might be to learn these tangential issues, so:
If you already have a single precision (float), then you can get the length by converting to a string using Single.ToString() or string.Format() and using string.Length on the result, though it will include the decimal point, so account for that.
See the ToString() and format specifiers at:
http://msdn.microsoft.com/en-us/library/fzeeb5cd(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/0c899ak8(v=vs.110).aspx
The problem with this is by the time you use ToString() you already have a valid float and by that time the check is moot. You need to check the original value. See (2)
If you are starting from a string (which in this sample you are reading from console to string, then parsing with Single.Parse(), then you will either get a valid value, or an exception. You need to use a try-catch block with Single.Parse(), otherwise switch to Single.TryParse() and check the boolean return value.
Lastly, if you want to ensure you can both parse the value, as well as validate numbers of greater precision or range, you may add a Double.TryParse() as well.
if(!Single.TryParse(str, ref snum)) {
if(Double.TryParse(str, ref dnum))
// valid number is out of range for Single
else
// valid number is out of range for Double, or invalid
}
Or you may use Single.Parse and catch(OverflowException)
try {
snum = Single.Parse(str);
}
catch(OverflowException e) {
}
All that was said regarding your actual question, but the spirit of the problem is how to compare 2 valid numbers, in my opinion. In that case, use TryParse() to valid them, then just compare them directly, or use approaches given in #drf's answer https://stackoverflow.com/a/24482343/257090

If the intent is to ensure that two floating-point numbers are approximately equal within 1E-7, there is no need to convert to a string at all. In fact, using string conversions is probably not a robust approach:
The question as written requires seven significant figures after the decimal point, not seven significant figures total. The length of the string will include both.
Single.ToString by default only prints 7 digits of precision, but stores up to 9 internally. The floats 20.000001 and 20.000003 will both convert to "20" by default, but are not equal within 1e-7. (This is a function of the string conversion, not precision limits with this datatype.)
Floats can be parsed and output in exponential notation. What happens if a user enters 1e+20 at the prompt, a perfectly-valid float with a length of 5?
Given two numbers f1 and f2, and an accepted error ε of 10^-7, it follows mathematically that f1 and f2 should be considered equal (within accepted tolerance) if and only if:
|f1 - f2| ≤ ε
Or in C#, we can write the equivalent function:
// preconditions: e is non-negativeps, f1 and f2 are not NaN, Inf, or -Inf.
bool AreApproximatelyEqual(float f1, float f2, float eps)
{
return Math.Abs(f1 - f2) <= eps;
}
Given floats firstNum and secondNum, approximate equality is obtained as:
bool equal = AreApproximatelyEqual(firstNum, secondNum, 1e-7f);

Related

How to remove the leading 0s and divide the remaining value by 100, use a dot separator between the integer and the decimal part in C#

for example let's just say I have a:
var value = "0000000000002022"
how can I get : 20.22
Mathematically speaking, it doesn't matter how many zeroes are before your number, their the same, so 0000002 = 2 is true. We can use this fact to simply parse our string to a number, and then do the division, but we have to be a little careful in which number type we use, because doing (int) 16 / (int) 5 will result in 3, which obviously isn't correct, but integer division does that. So, just to be sure we don't loose any precision, we'll use float
string value = "0000000000002022";
if (float.TryParse(value, out var number))
{
// Successfully parsed our string to a float
Console.WriteLine(number / 100);
}
else
{
// We failed to parse our string to a float :(
Console.WriteLine($"Could not parse '{value}' to a float");
}
Always use TryParse except if you're 110% sure the given string will always be a number, and even then, circumstances can (and will, this is software development after all) change.
Note: float isn't infinitely large, it has a maximum and minimum value, and anything outside that range cannot be represented by a float. Plus, floating point numbers also have a caveat: They're not 100% accurate, for example 0.1 + 0.2 == 0.3 is false, you can read up more on the topic here. If you need to be as accurate as possible, for example when working with money, then maybe use decimal instead (or, make the decision to represent the money as a whole number, representing the minor units of currency your country uses)
by using convert
Int16.Parse(value);
Convert.ToDecimal(int1)/100;

C# Float.ToString("0.###") cuts decimal numbers / rounds number to int

I have an object with a float property that I want to place into a text field. The problem is, that if I normally transform it with ".ToString()" it is written with scientific notation i. e. "1.5E+07" (And it cuts of all my decimal points).
Because of that I wanted to transform it with the optional number parameter "#" and so I wrote ".ToString("0." + new string('#', 339)" to guarantee that it only cuts the useless zeros from my float, but writes all other numbers. No matter whether I use that or just "0.###" it always cuts all my decimal numbers. I. e. "-3740295.25" (float) turns into "-3740295" (string).
I really don't understand why this is happening and didn't find anything similar on the internet and most conversion are about doubles to strings anyway(which also doesn't work for me).
EDIT:
As requested here is a code snippet, which made me realize, if I create a completely fresh float it just works, I don't know why it doesn't work with the float from the object:
float testFloat1 = 1234.5544f;
float testFloat2 = (keyValuePair.Value as JsonFloat).Property; // -3740295.25
string testString1 = testFloat1.ToString("0.###"); // 1234.554
string testString2 = testFloat2.ToString("0.###"); // -3740295
This is the object in short:
public partial class JsonFloat : JsonValue
{
public float Property { get; set; }
public JsonFloat(float property)
{
Property = property;
}
}
Kind Regards
Main issue here is that float which maps to System.Single simply does not have enough precision.
And precision does not mean the digits after the decimal point, precision means the whole number.
Here, let me give you an example:
float f = 1234567890f;
Console.WriteLine(f.ToString("################"));
Output:
1234568000
As you can see, the number got "truncated" and rounded up, only about the 7 most significant digits are kept with a float.
This is also why it worked better with double, but this also have limited precision, just a high limit.
double d = 12345678901234567890d;
Console.WriteLine(d.ToString("##############################"));
Output:
12345678901234600000
As you can see here, double kept more of the digits but then gave up at about 15 significant digits.
You can see the limits specified in the documentation of System.Single and System.Double.
Even though System.Single says:
A Single value has up to 7 decimal digits of precision, although a maximum of 9 digits is maintained internally
(my emphasis)
those 9 digits might be temporary while the number is in flight during expression evaluation. The processor registers are slightly bigger and thus slightly more precise than the data types the values are ultimately stored into.
I don't know why, but if I place the float inside a double it just works. So this works:
double testFloat2 = (keyValuePair.Value as JsonFloat).Property; // -3740295.25
string testString2 = testFloat2.ToString("0.###"); // -3740295.25
If anyone knows why, then I would appreciate an explanation

Why does float.TryParse() accepts "1.000e-45" and rounds it to 1.401298E-45?

I try to validate that an input string is a float. To achieve this I pass the input string like "1.000e-45" which is closer to 0 than the c# float minimum allows (1.401298E-45). I expect float.tryParse to return false. Instead it returns true and the parsed number is rounded up to the float minimum (1.401298E-45). If the string contains an even lower number, float.TryParse() seems to round down to 0.
Do I miss something?
What could I do to validate that low numbers like this are not interpreted as float?
var isFloat = float.TryParse("1.000e-45", NumberStyles.Float, CultureInfo.InvariantCulture, out var number);
Update: the user input MUST be a float in a string. it is not allowed to be of higher precision because of a specification. Just imagine it as some kind of API which gives me a string which should be a float value. I have to validate that it is in the ranges of a float.
Update 2: this is not a question about float precision. I know that you cannot have 100% precision because of the binary aspect and the size of the mantisse. As i stated in the question, i know the most close value to 0 for a float. I just didnt expect TryParse() to do rounding like this when its close to 0. I expected it to fail.
Update 3: I hope this makes it more clear. That is the specification that i have to follow.
The input is a string. the string represents a number. like "3.45e12"
the number is not allowed to be outside of the range of "±3.4028235e+38".
the number is not allowed to be smaller in magnitute than "±1.401298e-45". That is what the specification says."0" is allowed too, but not something like "1e-100" or "1e-46".
What i want to do is validate that the user input (a string) fullfills this specification. I dont have to convert the string number into a c# number type and calculate with it. I just have to validate the input string number and then i send the string number to an API. This api would have problems when the string does not match the specification.
I tried to use float.TryParse() for this validation. It works for the min/max ranges (.Net Framework). But it seems to be the wrong approach. float.TryParse() will always return true for number strings that are closer to 0 than it can actually represent and it will round the result to fit into a float: it rounds down to 0 (e.g. for "1e-46") or rounds up to 1.401298E-45 (e.g. for "1e-45". It makes sense to some point, because it does the same when it can not handle the precision of a value like "999999999999999.9" which is not above max and not too close to 0. It rounds it to 1e13.

Get individual digits from an Int without using strings?

I know you can convert the Int to a string and get the digit at position x using the indexer as if it was a char array, but this conversion becomes a bit of an overhead when you're dealing with multiple large numbers.
Is there a way to retrieve a digit at position x without converting the number to a string?
EDIT:
Thank you all, I will benchmark the proposed methods and check if it is any better than converting to a string. Thread will stay unanswered for 24h in case anyone has better ideas.
EDIT 2:
After some simple tests on ulong numbers, I have concluded that converting to strings and extracting the digit can be up to 50% slower compared to the methods provided below, see approved answer.
You could do something like this:
int ith_digit(int n, int i) {
return (int) (n / pow(10, i)) % 10;
}
We can get the ith digit by reducing the number down to a point where that digit we want becomes in the one's place, example:
Let's say you wanted the third digit in 12345, then by reducing it to 123 (by dividing it by 10 i number of times) we can then take the remainder of that number divided by ten to get the last digit, which is the digit we wanted.

Rounding the SIGNIFICANT digits in a double, not to decimal places [duplicate]

This question already has answers here:
Round a double to x significant figures
(17 answers)
Closed 7 years ago.
I need to round significant digits of doubles. Example
Round(1.2E-20, 0) should become 1.0E-20
I cannot use Math.Round(1.2E-20, 0), which returns 0, because Math.Round() doesn't round significant digits in a float, but to decimal digits, i.e. doubles where E is 0.
Of course, I could do something like this:
double d = 1.29E-20;
d *= 1E+20;
d = Math.Round(d, 1);
d /= 1E+20;
Which actually works. But this doesn't:
d = 1.29E-10;
d *= 1E+10;
d = Math.Round(d, 1);
d /= 1E+10;
In this case, d is 0.00000000013000000000000002. The problem is that double stores internally fractions of 2, which cannot match exactly fractions of 10. In the first case, it seems C# is dealing just with the exponent for the * and /, but in the second case it makes an actual * or / operation, which then leads to problems.
Of course I need a formula which always gives the proper result, not only sometimes.
Meaning I should not use any double operation after the rounding, because double arithmetic cannot deal exactly with decimal fractions.
Another problem with the calculation above is that there is no double function returning the exponent of a double. Of course one could use the Math library to calculate it, but it might be difficult to guarantee that this has always precisely the same result as the double internal code.
In my desperation, I considered to convert a double to a string, find the significant digits, do the rounding and convert the rounded number back into a string and then finally convert that one to a double. Ugly, right ? Might also not work properly in all case :-(
Is there any library or any suggestion how to round the significant digits of a double properly ?
PS: Before declaring that this is a duplicate question, please make sure that you understand the difference between SIGNIFICANT digits and decimal places
The problem is that double stores internally fractions of 2, which cannot match exactly fractions of 10
That is a problem, yes. If it matters in your scenario, you need to use a numeric type that stores numbers as decimal, not binary. In .NET, that numeric type is decimal.
Note that for many computational tasks (but not currency, for example), the double type is fine. The fact that you don't get exactly the value you are looking for is no more of a problem than any of the other rounding error that exists when using double.
Note also that if the only purpose is for displaying the number, you don't even need to do the rounding yourself. You can use a custom numeric format to accomplish the same. For example:
double value = 1.29e-10d;
Console.WriteLine(value.ToString("0.0E+0"));
That will display the string 1.3E-10;
Another problem with the calculation above is that there is no double function returning the exponent of a double
I'm not sure what you mean here. The Math.Log10() method does exactly that. Of course, it returns the exact exponent of a given number, base 10. For your needs, you'd actually prefer Math.Floor(Math.Log10(value)), which gives you the exponent value that would be displayed in scientific notation.
it might be difficult to guarantee that this has always precisely the same result as the double internal code
Since the internal storage of a double uses an IEEE binary format, where the exponent and mantissa are both stored as binary numbers, the displayed exponent base 10 is never "precisely the same as the double internal code" anyway. Granted, the exponent, being an integer, can be expressed exactly. But it's not like a decimal value is being stored in the first place.
In any case, Math.Log10() will always return a useful value.
Is there any library or any suggestion how to round the significant digits of a double properly ?
If you only need to round for the purpose of display, don't do any math at all. Just use a custom numeric format string (as I described above) to format the value the way you want.
If you actually need to do the rounding yourself, then I think the following method should work given your description:
static double RoundSignificant(double value, int digits)
{
int log10 = (int)Math.Floor(Math.Log10(value));
double exp = Math.Pow(10, log10);
value /= exp;
value = Math.Round(value, digits);
value *= exp;
return value;
}

Categories

Resources