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

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; }
}

Related

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?

How to make JavaFX Chart NumberAxis only show Integer value,not double

I'm trying to create a chart who's yAxis is designed to show number of employee number, so it must only show whole numbers.
But I found it's not that easy as I already tried to yAxis.setTickUnit(1) but it won't work when the values are small(etc. the max value is 3, it'll still show 0.5,1.5..., I only want tick value like 1,2,3,4..)
How Could I to achieve this?
According to #jewelsea 's answer, I tried this(In javafx 2.2 jdk7)
class IntegerStringConverter extends StringConverter<Number>{
public IntegerStringConverter() {
}
#Override
public String toString(Number object) {
if(object.intValue()!=object.doubleValue())
return "";
return ""+(object.intValue());
}
#Override
public Number fromString(String string) {
Number val = Double.parseDouble(string);
return val.intValue();
}
}
It's result is kind of acceptable. Double value's are gone, but there ticks are still there.
Set a tickLabelFormatter on the axis.
You can set the NumberAxis(double lowerBound, double upperBound, double tickUnit) values by using the constructor. This works fine for JaxaFX 8, in case people like me are still looking for this.
Source:
https://docs.oracle.com/javase/8/javafx/api/javafx/scene/chart/NumberAxis.html

Float inaccurate rounding in Mongodb with CS driver

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.