Float inaccurate rounding in Mongodb with CS driver - mongodb

When I save the float number into MongoDB using csharp driver it is not saved accurately. If my number is 1504.57 I expect the database will have the same number but for some reason it become 1504.56994628906 (with Double type in MongoDB). What's going on? How to keep data accurately?
My object keep all the field as object types and cast them on the fly depending on the type, for example:
this.Values[i] = float.Parse(this.Values[i].ToString());
Maybe is it the reason of this strange behavior? But after casting this.Values[i] is pretty accurate and it's spoiled only in database.
Thanks
Update.
The class that incapsulates data:
public class TransferredData
{
[BsonElement("_id")]
[ScriptIgnore]
public ObjectId Id { get; set; }
public class Data
{
public List<Object> Values { get; set; }
public DateTypes DataType { get; set; }
public void CastToType()
{
for (int i = 0; i < this.Values.Count; i++ )
{
if (this.DataType == DateTypes.Date)
{
DateTime dt = DateTime.Parse(this.Values[i].ToString());
this.Values[i] = dt.ToUniversalTime().Date;
}
else if (this.DataType == DateTypes.Other)
{
this.Values[i] = this.Values[i].ToString();
}
else if (this.DataType == DateTypes.Reading)
{
this.Values[i] = float.Parse(this.Values[i].ToString());
}
}
}
}
}
I use Object type because I don't know which actual type it could be. So, just before doing upsert I fill up this class by data and then call Cast method.
Then I save it into db:
data = new TransferredData();
...
data.Values[1] = "1504.57"; // Because the input is always string
data.CastToType(); // Here data.Values[1] = 1504.57 - cool
TransferredDataCollection.Save<TransferredData>(data, SafeMode.True);
After this moment, looking into database... it's 1504.56994628906

IEEE 754 floating point formats cannot represent every single number accurately. As such your issue cannot be avoided and works as intended.
You should not ever use floating point formats if you need absolute guarantees that input=output while you cannot guarantee that the input can be accurately represented by the used floating point format.
Most people run into these problems when they try to store monetary values in floats which is universaly accepted to be an extremely bad idea. If you need to store monetary values save it as an integer value, usually as cents.

i imagine this has something to do with the representatioh of 1504.57 as a double precision floating point #.
perhaps there are a couple options, one being to round off the excess digits and the other being to store cents instead of dollars if the number is a currency #.

I would echo all the above comments that floating point numbers often exhibit interesting rounding errors when converted from decimal to binary and back.
In your particular case, the rounding errors are being introduced by the C# language itself, not by the C# driver. MongoDB does not natively support single precision floating point numbers, so your C# float values are being converted by the C# driver to doubles before being sent to the database. It is during this conversion that the rounding errors occur.
Here's a small snippet of C# code that demonstrates how casting a float to a double introduces the rounding errors:
var f = (float)1504.57;
var d = (double)f;
Console.WriteLine(f);
Console.WriteLine(d);
As others have recommended, if you must have 100% precision you should use some other data type than float or double.

Related

How do I use STL std::list with objects?

I want to create linked lists of objects sorted by an object attribute (physical size); but so far it seems I will have to code them myself...
These lists will be short, typically a dozen nodes each; but I may have up to a thousand lists; so I can't afford the extra weight of using std::map's. In fact, I'd be quite happy with single linked list.
But I need the node to be more than just a value.
The key value in my objects is rarely going to change; however elements will have to come out of one list and move to another quite often.
((Actual use: One list per quad, in a quad-tree (as for collision detection, etc); objects sorted by size, as the larger objects are less numerous but need to be picked quickly from larger ranges, so they should come up first in the lists.))
But every example I find for using std::list to maintain sorted lists uses a list of integers as the example; but that's not very useful; what I have is objects that have one value member to be sorted by.
I was thinking of using lower_bound to find the insertion point, then insert the object; but the lower_bound iterator takes a begin, and end, and a plain value as the third argument; I see no mechanism by which I can specify to use a particular member of my objects to sort by.
I could, of course, define a conversion operator,
my_object_type::int(){ return sortby; }
Would that work? Is there a better way?
I seem to have found my answer in this reference:
https://www.geeksforgeeks.org/lower_bound-in-cpp/
under "Syntax 2"; there is provision for a fourth
argument being a comparison functor. So, something
like this should work (not tested yet):
class pnt_proxy
{
int x; //position
int y;
point* real_pnt; //the real point this represents
public:
float sz; //rough largest diagonal across object
}
class pproxy_cmp : public std::binary_function< pnt_proxy, pnt_proxy, bool >
{
public:
bool operator()( pnt_proxy const & a, pnt_proxy const & b ) const
{
return a.sz < b.sz;
}
};
std::list< pnt_proxy > ll;
void insert_sorted( pnt_proxy const & pp )
{
if( ll.size() )
{
std::list<pnt_proxy>::iterator insert_at;
insert_at =
std::lower_bound( ll.begin(), ll.end(), pp, pproxy_cmp() );
ll.insert( insert_at, pp );
}
else ll.push_back( pp );
}

get double value from bigdecimal without exponential java

is there a way to get double from BigDecimal without exponential?
Eg: I have an new BigDecimal("123456797676.897")
if i print new BigDecimal("123456797676.897").toString() it prints properly as 123456797676.897.
Now suppose if I try to print new BigDecimal("123456797676.897").doubleValue(), it prints with exponential like 1.23456797676897E11.
Is there any way I can get doublevalue with out exponential.
Thanks :)
The following program demonstrates that these are all exactly the same double:
new BigDecimal("123456797676.897").doubleValue()
123456797676.897
1.23456797676897E11
12.3456797676897E10
The Double toString method has to pick one representation for each value. For numbers greater than 107 it uses exponential notation with a single digit before the decimal point. That is a generally reasonable choice, but it is not always the right choice. If you want it displayed without the exponent use DecimalFormat. If you are just using the number, it makes no difference whether Double's toString would have displayed it with an exponent or not, and you don't need to do anything about it.
import java.math.BigDecimal;
public class Test {
public static void main(String[] args) {
double doubleValue = new BigDecimal("123456797676.897").doubleValue();
double simpleLiteral = 123456797676.897;
double exponent11Literal = 1.23456797676897E11;
double exponent10Literal = 12.3456797676897E10;
System.out.println(doubleValue == simpleLiteral);
System.out.println(doubleValue == exponent11Literal);
System.out.println(doubleValue == exponent10Literal);
}
}
output:
true
true
true

How to compare a date in entity framework to avoid rounding errors?

I have a .NET DateTime value that I write to a SQL Server database "datetime" field (and trust me, I WISH we were just using "datetime2(7)" that matches .NET's DateTime precision exactly, but we're not).
Anyway, I write the entity to the database and that particular field ends up being '2016-03-03 08:55:19.560'.
It's a last processing time, and I'm looking for other records that were processed before that time. When I run an entity framework where clause, it ends up running a statement ending with "#p__linq__0='2016-03-03 08:55:19.5602354'" as the value it's comparing against, which ends up being slightly greater, even though these two values originate from the exact same DateTime instance.
I tried changing the time it's comparing against to an SqlDateTime, but then the lambda doesn't compile because it can't compare a DateTime? to a SqlDateTime. SqlDateTime has comparison methods, but I don't know whether entity framework recognizes the functions.
I can't even cast between the two in entity framework, which just gives the error "Unable to cast the type 'System.DateTime' to type 'System.Data.SqlTypes.SqlDateTime'. LINQ to Entities only supports casting EDM primitive or enumeration types."
I face the same problem. In my case I finally value the DateTime fields by using:
public static DateTime RoundedToMs(this DateTime dt) {
return new DateTime(dt.Ticks - (dt.Ticks % TimeSpan.TicksPerMillisecond), dt.Kind);
}
public static DateTime RoundedToMsForSql(this DateTime dt) {
DateTime n = dt.RoundedToMs();
return new DateTime(n.Year, n.Month, n.Day, n.Hour, n.Minute, n.Second, (n.Millisecond / 10) * 10);
}
And in the business code:
someEntity.SomeDate = dateValue.RoundedToMsForSql();
The point, in my case, is sql datetime has a 3ms precision, so I decided to remove millisecond unit.
Same extension may be used in the queries, well in fact to populate variables used in the queries.
var d = DateTime.Now.RoundedToMsForSql();
var q = from e in ctx.Entities where e.SomeDate <= d;

Can a composite format in String.Format() be used to return a substring?

I apologize for what seems to be an exceedingly simple question, but after 4 hours of searching and beating my head against the wall, I'm doubting my sanity.
I need a string format expression that trims a supplied string argument much like Substring(0,1). Although I've never seen this in code, it just seems like it should be possible.
Here's a very basic example of what I'm trying to do:
string ClassCode = "B608H2014"; // sample value from the user's class schedule
string fRoom = "{0:0,2}";
string fGrade = "{0:2,2}";
string fYear = "{0:5,4}";
string Classroom = String.Format(fRoom, ClassCode); // intended result - "B6"
string Gradelevel = String.Format(fGrade, ClassCode); // intended result - "08"
string Schoolyear = String.Format(fYear, ClassCode); // intended result - "2014"
This is a very basic example, but I'm trying to use the String.Format() so I can store the format pattern(s) in the database for each respective DTO property since the class code layouts are not consistent. I can just pass in the formats from the database along with the required properties and it extracts what I need.
Does this make sense?

MongoDB C# offi : Lat, Lng as doubles are stored at precision13 and rounding?

I store lat and lng as double in MondoDB and C# official driver. I have problems with my points that are not matching the right places after a rounding of double values internaly in MondoDB(by driver or intern). I have searched all posible rounding before post to the Repository but havenĀ“t found anything. Why not use decimal, because Query.Near use doubles.
v1.6.5 ; C#v0.11 ; Win64.
XX.444057295828145;XX.63416004180907 Captured as string
XX.444057295828145;XX.63416004180907 Receipt by Repository before Save method
Inside MongoDB and returned :
XX.4440572958281, XX.6341600418091, Saved and returned (only 13 after dot)
This is resulting to a moved map. Advices. Thanks.
I use this Update method
public void SaveGeoPoints(String id, String lat, String lng)
{
//--> XX.444057295828145;XX.63416004180907
Db.repository.Update(
Query.EQ("_id", id),
Update.Set("Lat", double.Parse(lat))
.Set("Lng", double.Parse(lng)));
}
the default format for Console.WriteLine prints only 13 digits after the decimal point. The following code snippet shows this
var test = "10.444057295828145";
Console.WriteLine(test);
var testInDouble = double.Parse(test);
Console.WriteLine(testInDouble);
Console.WriteLine(testInDouble.ToString("R"));
Console output is
10.444057295828145
10.4440572958281
10.444057295828145
I am unable to reproduce what you are describing. I can save and retrieve those values from MongoDB without loss of precision. Here's the test code I used:
http://www.pastie.org/1599603
As an additional test I verified the contents of the documents in the database using the Mongo shell.
There must be some rounding going on somewhere else. I would check all places where you convert back and forth between doubles and strings.
Here's another test that uses the Update.Set method:
http://www.pastie.org/1599777
I still see no loss of precision when storing values to the database and reading them back.
You don't say what your typical values of XX are. I'm using 12 in the test.
Note that double only has a total precision of slightly over 15 decimal digits (doesn't matter where the decimal point is), so the larger your values of XX the less precision that is left over to use to the right of the decimal point.
If you are simply exceeding the precision limits of the 64 bit IEEE double type that is not anything to do with either the C# driver or MongoDB.
Thanks Sam and bugai for this help. Of course all answers are corrects. It's a limits of C# double and printings in C# like point Bugai in upper post. I post here the logical used to others users like me that lost the North with this amount of decimals. Not use double? is not accepting round-trip format. Use round-trip formatter in your outputs to Api. Thanks for comprension. Get Location now having 15 decimals. P.R
public class BsonCentroid
{
[BsonId]
public String Id { get; set; }
public String Location
{
get { return String.Format("{0};{1}", Lat.ToString("r"), Lng.ToString("r")); }
}
public double Lat { get; set; }
public double Lng { get; set; }
}