Let's say I have a rails model called Position and the two columns latitude and longitude (both defined as float). I have populated the model with some position values. Now I'm trying to find positions based on it's latitude:
positions = Position.where('positions.latitude = ?', 50.0)
returns nil.
Even if I try this:
lat = Position.first.latitude
positions = Position.where('positions.latitude = ?', lat)
the result is nil. My database is mysql for production. The code above words in development (sqlite). My assumption is it has something to do with how the datatypes are handled but I dont get it. Anyone some ideas?
Don't use FLOAT or DOUBLE when you need exact numeric values.
Read https://dev.mysql.com/doc/refman/8.0/en/problems-with-float.html
Instead, use DECIMAL or NUMERIC (they're synonyms).
See How accurately should I store latitude and longitude? for some guidance on the specific DECIMAL precision to use for latitude and longitude.
This is maybe due to how floating point numbers are represented. It's not possible to compare them by using a normal equality comparison. Instead of checking for the exact match it's better to check if the number is in a range or round both numbers to some precision before comparing them.
Regarding the particular example with geographical coordinates (I assume this because latitude is used), I would recommend using decimal column type instead of float.
In case you would like to stick with floatcolumn, I can suggest checking if the value is in a range.
Related
I have a database with a bunch of LineString's and I want to see if a Point is within, say, 50ft of that line.
Since it's a LineString, expecting ST_CONTAINS to return true for a point that's even 1ft away from the LineString seems unlikely BUT if there was a way to add an accepted error margin that'd be cool.
Here's the query I did:
SELECT *
FROM railroads
WHERE ST_CONTAINS(SHAPE, ST_GeomFromText('POINT(-10874300.116373 3537642.0497826)', 3857));
Unsurprisingly, it returns no results.
Any ideas? I'm running MySQL 8.0.22.
You need a condition like
ST_Distance(shape, geo_constant) < distance.
Another pattern to do it is
ST_Intersect(shape, ST_Buffer(geo_constant, distance)).
Buffer is expensive, so make sure you buffer a single constant, not the table column.
But there is another issue here, you are using projection 3857, so the distance is in projection units, not feet or meters. If you care about precision, transform Geometry to Geography type.
MSDN documentation just defines the return value as:
A double value that specifies the distance between the two closest
points in this geometry value and other.
What units are returned? When testing the values do not match to anything I recognise and does not work in a WHERE clause.
var results = db.Locations.Where(t => t.ItemPoint.Distance(targetPoint) <= 100); // 100km
Note: I am using MySQL with Entity Framework using the .NET Connector, which doesn't support using SRID values.
Whilst the MSDN documentation could do with a little more information, it doesn't specify the measurement of the value as it changes dependant on the SRID (or lack thereof).
Without an SRID, Geometry simply works on a flat grid (or planar) system (think old school square paper). Your distance answers are therefore simple pythagoras results of
a2 + b2 = c2 >>>>>> c = SqRt(a2 + b2).
For example:
using System.Data.Entity.Spatial;
DbGeometry geom1 = DbGeometry.FromText("POINT(0 0)");
DbGeometry geom2 = DbGeometry.FromText("POINT(50 50)");
Console.WriteLine(geom1.Distance(geom2));
//Returns 70.71067811 (the square root of (50 sq'd + 50 sq'd)).
You'll find this blog an interesting read, skip to halfway down the page and you'll find how to create a user-defined haversine calculation method which will allow you to convert the results to distances.
Be warned though, If you can't map Stored Procedures in EF for MySQL, then you'll need to resort to good old fashioned ADO to get the results. Make sure you use st_distance over mbr_distance as st_distance will be exact and mbr_distance will contain false positives. You will need MySQL 5.6.1 for st_distance.
Lastly, if at all possible, consider another database if Spatial data is important.
This question already has answers here:
How to deal with floating point number precision in JavaScript?
(47 answers)
Unexpected Output when adding two float numbers
(3 answers)
Closed 10 years ago.
Good afternoon.
Noticed the strangeness in calculating the amount in the field.
Vaues table:
Type field - float.
I make select sum:
SELECT SUM(cost) as cost FROM Table
In result i get sum = 20.47497010231018;
I use calculator) and i get sum = 20,47497
Tell me please why do I get different results?
Here, read this: http://floating-point-gui.de/
So, the problem is that floating point numbers - the internal implementation for decimal numbers in hardware, and used by most languages and applications, does not map 1 to 1 to numbers in base 10, as we are used. The underlying values are expressed in base 2, and have a limited precision. Some numbers that can be represented with few digits in the decimal notation can actually need much more digits in the native format - leading to rounding errors.
The article linked above explains it in detail.
Use DECIMAL(16, 4) (for example) type for your currency columns, where 4 is the number of digits after the .
You will have to trim the trailing zeros though, since for instance 10 would appear 10.0000. This is however easily done.
These kinds of differences are not enough to be worried about in most situations.
Floating point numbers are notorious for being slightly off of the intended values because of the number of bits and how floating point numbers are stored in binary. For example, as demonstrated here, a decimal value of 0.1 has an actual double value of 0.10000000149011612. Close, but not exact.
A technique I've seen used in some applications that need absolutely accurate latitude and longitude numbers is that they'll keep the values in integral data types that are equivalent to the float multiplied by some power of 10. For example, a GeoPoint in the Google Maps API v1 on Android measures in micro degrees. Instead of a latitude and longitude like 54.123428, 60.809234 to preserve the values precisely they'd be ints: 54123428, 60809234. To depict this, they'd call the variables latitudeE6 and longitudeE6 to indicate that it's the real latitude or longitude multiplied by 1E6 (the scientific notation of 10^6).
I have latitude and longitude columns in my table.
I'm currently storing latitude with float(16,14) and longitude with float(17,14). Is that the best way to store them? The values I'm inserting are from JS navigator.geolocation, and they don't tend to have more than 14 digits after the decimal place.
Should I be using decimal instead?
You should use decimal! Read on this: Problems with Floating-Point Values
The DECIMAL type stores exact numeric data values (if you use MySQL5.0.3 or above), the FLOAT type represents approximate numeric data values.
Since as far as I know latitude and longitude values are usually exact numeric data values for most use cases, I'd go for a DECIMAL.
What's the best type to store values such:
48.89384 and -2.34910
Actually I'm using float.
Use decimal for exact values.
Notes:
ABS (Latitude) <= 90
ABS (Longitude) <= 180
So you can us 2 different types
Latitude = decimal (x+2, x)
Longitude = decimal (y+3, y)
x and y will be the desired precision. Given a metre is 1/40,000,000 of the earth's circumferemce, something like 6-8 will be enough depending on whether you're going for street or full stop accuracy in location.
If you want exact representation, and you know the scale that applies, then you can use the decimal data type.
If you work with money use DECIMAL type. It has no floating-points inaccuracy.
#MiniNamin
if you are using sql then it will also work by putting the DataType Numeric(18,4)
The benefit of floating point is that it can scale from very small to very large numbers. The cost of this, however, is that you can encounter rounding errors.
In the case that you know exactly what level of accuracy you need and will work within, the numeric / decimal types made available to you are often more appropriate. While working within the level of accuracy you specifify on creation, they will not encounter any rounding errors.
That depends on what you're using the numbers for. If these numbers are latitude and longitude, and if you don't need exact representations, then FLOAT will work. Use DOUBLE PRECISION for more accuracy.
But for exact representations, use DECIMAL or NUMERIC. Or INT or one of its different sizes, if you'll never have fractions.