1
var x = dr["NationalTotal"].ToString();

gives me 333333333

var xxx = Convert.ToSingle(dr["NationalTotal"].ToString());

gives me 333333344

Any ideas why?

Zbigniew
  • 27,184
  • 6
  • 59
  • 66
punkouter
  • 5,170
  • 15
  • 71
  • 116
  • You shouldn't be calling `ToString()`. (this will not fix anything) – SLaks Oct 24 '13 at 17:04
  • I need it in a float float nationalTotal = dr["NationalTotal"].ToString().Trim() == "" ? 0.0f : Convert.ToSingle(dr["NationalTotal"].ToString()); – punkouter Oct 24 '13 at 17:06
  • Compare to `DBNull.Value` not empty string. – Dustin Kingen Oct 24 '13 at 17:07
  • Limited precision with which floating point numbers can be represented. Only 24 bits - so the largest integer that is not rounded is 2^24, or about 8000000. – Floris Oct 24 '13 at 17:07
  • How do I get it to a float ? The reason I am trying to make it a float is because it is a 'float' in the sql db .. This does not compile ... float xxxx = Convert.ToDouble(dr["NationalTotal"]); – punkouter Oct 24 '13 at 17:12
  • What database are you using? That is important information in order to answer the question you added in your comment (maybe add it in the body of the question...). You don't just want to know "why" (ans: "float does not have enough precision") but "what do I do about it?". Some flavors of SQL allow you to specify the precision anywhere from 1 to 53 bits... http://technet.microsoft.com/en-us/library/ms173773.aspx but to use this you must use the right conversion. Which requires knowing what database you are using and how you inject the data into it. – Floris Oct 24 '13 at 17:34
  • I need to do this // float xxxx = Convert.ToDouble(dr["NationalTotal"]); but that will not compile.... is the 'float' type for the database column the thing that should be changed? – punkouter Oct 24 '13 at 17:36
  • When you say "it will not compile", can you show the shortest complete program that doesn't compile? `ToDouble()` returns a `double`, not a `float` so there will be some implicit conversion (which your compiler may complain about). But unless your database precision is specified to be sufficient (more than 24 bits for mantissa), it doesn't matter how precise you are in converting the string input. Does that make sense? You may find [this earlier question](http://stackoverflow.com/questions/1209181/what-represents-a-double-in-sql-server) useful. – Floris Oct 24 '13 at 17:43
  • This line with not compile.. though toDouble does return the correct value.. I need to get the correct value in a float (unless I change the database type) ......... float xxx = Convert.ToDouble(dr["NationalTotal"]); – punkouter Oct 24 '13 at 17:45
  • 1
    The database type `float` is in fact a `double` (see http://msdn.microsoft.com/en-us/library/system.data.sqldbtype.aspx). I would change that line to `double xxx = Convert.ToDouble(dr["NationalTotal"]);` and then insert `xxx` into the database. I think that will work. – Floris Oct 24 '13 at 17:47
  • THANK YOU SIR you are correct. All I had to do is stop using float in code.. I could use double and EF had no problem with it.. and now it has the correct value! – punkouter Oct 24 '13 at 17:58
  • Glad I was able to help you solve your problem. I have updated my answer with some additional information for completeness. Bit surprised about the answer you chose to accept... – Floris Oct 24 '13 at 18:09

3 Answers3

6

This happens because Single does not have enough precision to store your full number.

Double has more precision.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
4

From the docs for System.Single:

All floating-point numbers have a limited number of significant digits, which also determines how accurately a floating-point value approximates a real number. A Double value has up to 7 decimal digits of precision, although a maximum of 9 digits is maintained internally.

It actually means Single here rather than Double, but the point is that you're trying to use it for 9 significant digits, and that's not guaranteed to be available. You can see this very easily without any parsing:

float f = 333333333f;
Console.WriteLine("{0:r", f); // Prints 333333344

("r" is the "round-trip" format specifier.)

In other words, the closest float value to the exact decimal value of 333333333 is 333333344.

If you were to use Double instead, that would probably retain all the digits, but that doesn't mean it's the right approach necessarily. It depends on the value. Is it actually always an integer? Maybe you should be using long. Is it a non-integer, but financial data (or some other "artificial" value)? If so, consider using decimal.

Also, why are you having to convert this to a string and parse it? What's the execution time type of dr["NationalTotal"]? You may be able to just cast - avoid using string conversions where you don't need to.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • its coming from converting a excel grid to a datatable using IExcelDataReader DataSet AsDataSet();... I assumed all the cells were strings.. maybe I a wrong.. ill check – punkouter Oct 24 '13 at 17:08
  • oh.. you are right.. it is actually [System.RuntimeType] = {Name = "Double" FullName = "System.Double"}... but I need to convert that to a float.. – punkouter Oct 24 '13 at 17:15
  • @punkouter: If you need to convert to `float`, but `float` doesn't have as much information as you need, you're in trouble. But you can just cast: `var x = (float)(double) dr["NationalTotal"];` - you need to cast twice because the cast to `double` is just unboxing. – Jon Skeet Oct 24 '13 at 17:40
  • that compiles and works but the value is still wrong (333333344).... float x = (float)(double)dr["NationalTotal"]; – punkouter Oct 24 '13 at 17:48
  • @punkouter: Well yes, of course it's still 333333344 - for the reasons in the rest of the answer. In particular: "the closest float value to the exact decimal value of 333333333 is 333333344". – Jon Skeet Oct 25 '13 at 11:43
4

The floating point specification says that the 32 bit representation of a floating point number is

enter image description here

Thus, the largest integer that can be represented without loss of (integer) accuracy is 16777216 (0x1000000). A simple test program to convince you that this is so:

#include <stdio.h>

int main(void) {
  float x;
  unsigned long j;
  j = 0x00FFFFFC;
  int i;
  x = j;
  for(i=0; i<10;i++)  printf("%ld + %d = %f\n", j, i, x+i);
}

Output:

16777212 + 0 = 16777212.000000
16777212 + 1 = 16777213.000000
16777212 + 2 = 16777214.000000
16777212 + 3 = 16777215.000000
16777212 + 4 = 16777216.000000
16777212 + 5 = 16777216.000000  <<< from here on, adding one more doesn't give the right answer
16777212 + 6 = 16777218.000000
16777212 + 7 = 16777220.000000
16777212 + 8 = 16777220.000000
16777212 + 9 = 16777220.000000

EDIT Based on the comments underneath the question, we slowly converged on the fact that you had two questions, not one.

The first: "Why is this happening?" is answered with the above. A single float simply is not able to represent 333333333 exactly, so you get the nearest representable value, which is 333333344.

The second: "How do I fix it?" was initially answered by me in the comments - I will reprise my answer here:

Your database floating point number is not typically single precision - in fact, by default it is double precision. Thus, you solve your problem by converting the string to double, and assigning it to a double precision variable:

double xxx = Convert.ToDouble(dr["NationalTotal"]);

I would like to refer you to http://floating-point-gui.de/ - "what every programmer should know about floating point". There are actually many guides out there with similar names. Essential reading if you ever stray from using just integers (and most people will, at some point in their programming career).

Floris
  • 45,857
  • 6
  • 70
  • 122