I have a MySQL table with column of type float(10, 6).
If I insert 30.064742 into the column the value stored in the database is 30.064741.
Why?
Floating-point numbers imply a certain amount of imprecision. Use a DECIMAL column if you need to be certain to retain every digit.
It's a general problem with rounding numbers to a precision which can be stored in the database. Floats will round to multiples of powers of two. If you want something that is easier to think about, you can use the Decimal type, which will round to powers of ten.
More details in the documentation for numeric types:
When such a column is assigned a value with more digits following the decimal point than are allowed by the specified scale, the value is converted to that scale. (The precise behavior is operating system-specific, but generally the effect is truncation to the allowable number of digits.)
Related
If we specify column as decimal(5,2), which means we have precision is 5 and scale is 2.
If I understand it correctly, precision is maximum number of digits in a number and scale is maximum number of digits that can be present to the right of the decimal point.
So from this logic if I try to insert 100.999 it should fail as scale is 3 for this number which is not allowed. But when I am using online edition such as:
https://www.tutorialspoint.com/execute_sql_online.php
https://www.mycompiler.io/new/sql
and running following queries:
CREATE TABLE nd (nv decimal(5, 2));
INSERT INTO nd VALUES(100.999);
INSERT INTO nd VALUES(1001.2);
INSERT INTO nd VALUES(10011.2);
INSERT INTO nd VALUES(100111.299999);
Select * from nd;
This gives me output as:
100.999
1001.2
10011.2
100111.2991999
Can anyone explain why this is the case?
The only database that would not complain and run your code is SQLite, because in SQLite there is no DECIMAL data type.
So, maybe you think that by defining a column as decimal(5, 2) you are defining the column to be numeric with 5 as precision and 2 as scale, but all of them are just ignored.
For SQLite you are just defining a column with NUMERIC affinity and nothing more.
The proper data type that you should have used is REAL and there is no way to define precision and scale (unless you use more complex constraints).
You can find all about SQLite's data types and affinities here: Datatypes In SQLite Version 3
Precision is the number of digits in a number. Scale is the number of digits to the right of the decimal point in a number. For example, the number 123.45 has a precision of 5 and a scale of 2.
Those online SQL executors probably uses SQLite, which ignores the decimal and corresponding scale and precision, as #forpas said.
If you try to execute the queries in database like postgresql, you'll get the error you expected, numeric field overflows exception.
Try this engine to execute your queries, hope you'll understand what is happening.
In Oracle when you have a NUMBER data type and do not specify precision and scale like NUMBER(18,2) for example and use it like this NUMBER instead it will store the value as given.
From the manual
If a precision is not specified, the column stores values as given
Now I want to know if there is a way to let NUMERIC data type in SQL Server do the same. I have to use NUMERIC and not DECIMAL or other data types and I am not allowed to specify the precision or scale since I have no possibility to test if the data that will be used will cause errors because I have no access to the data. I just know that the data did not cause any trouble with our Oracle database which uses only NUMBER datatype without any specifications.
No, numeric needs a precision and scale and has defaults if none are set. Simple like that.
https://learn.microsoft.com/en-us/sql/t-sql/data-types/decimal-and-numeric-transact-sql?view=sql-server-2017
Quote:
decimal[ (p[ ,s] )] and numeric[ (p[ ,s] )] Fixed precision and scale
numbers. When maximum precision is used, valid values are from - 10^38
+1 through 10^38 - 1. The ISO synonyms for decimal are dec and dec(p, s). numeric is functionally equivalent to decimal.
Like often, documentation is your friend.
I have a table ProductAmount with columns
Id [BIGINT]
Amount [FLOAT]
now when I pass value from my form to table it gets stored in format 2.46237846387469E+15 whereas actual value was 2462378463874687. Any ideas why this value is being converted and how to stop this?
It is not being converted. That is what the floating point representation is. What you are seeing is the scientific/exponential format.
I am guessing that you don't want to store the data that way. You can alter the column to use a fixed format representation:
alter table ProductAmount alter amount decimal(20, 0);
This assumes that you do not want any decimal places. You can read more about decimal formats in the documentation.
I would strongly discourage you from using float unless:
You have a real floating point number (say an expected value from a statistical calculation).
You have a wide range of values (say, 0.00000001 to 1,000,000,000,000,000).
You only need a fixed number of digits of precision over a wide range of magnitudes.
Floating point numbers are generally not needed for general-purpose and business applications.
The value gets stored in a binary format, because this is what you specified by requesting FLOAT as the data type for the column.
The value that you store in the field is represented exactly, because 64-bit FLOAT uses 52 bits to represent the mantissa*. Even though you see 2.46237846387469E+15 when selecting the value back, it's only the presentation that is slightly off: the actual value stored in the database matches the data that you inserted.
But i want to store 2462378463874687 as a value in my db
You are already doing it. This is the exact value stored in the field. You just cannot see it, because querying tool of SQL Management Studio formats it using scientific notation. When you do any computations on the value, or read it back into a double field in your program, you will get back 2462378463874687.
If you would like to see the exact number in your select query in SQL Management Studio, use CONVERT:
CONVERT (VARCHAR(50), float_field, 128) -- See note below
Note 1: 128 is a deprecated format. It will work with SQL Server-2008, which is one of the tags of your question, but in versions of SQL Server 2016 and above you need to use 3 instead.
Note 2: Since the name of the column is Amount, good chances are that you are looking for a different data type. Look into decimal data types, which provide a much better fit for representing monetary amounts.
* 2462378463874687 is right on the border for exact representation, because it uses all 52 bits of mantissa.
I have data for pounds and pence stored within concatenated strings (unfortunately no way around this) but can not guarantee 2 decimal places.
E.g. I may get a value of 119.109, so this must translated to 2 decimal places with truncation, i.e. 119.10, NOT 119.11.
For this reason I am avoiding "CAST as Decimal" because I do not want to round. Instead I am using ROUND(amount, 2, 1) to force truncation at 2 decimal places.
This works for the most part but sometimes exhibits strange behaviour. For example, 119.10 outputs as 119.09. This can be replicated as:
ROUND(CAST('119.10' AS varchar),2,1)
My target field is Decimal(19,4) (but the 3rd and 4th decimal places will always be 0, it is a finance system so always pounds and pence...).
I assume the problem is something to do with ROUNDing a varchar....but I don't know anyway around this without having to CAST and therefore introduce rounding that way?
What is happening here?
Any ideas greatly appreciated.
This is due to the way floating point numbers work, and the fact that your string number is implicitly converted to a floating point number before being rounded. In your test case:
ROUND(CAST('119.10' AS varchar),2,1)
You are implicitly converting 119.10 to float so that it can be passed to the ROUND function, 119.10 exactly cannot be stored as a float, proven by running the following:
SELECT CAST(CONVERT(FLOAT, '119.10') AS DECIMAL(30, 20))
Which returns:
119.09999999999999000000
Therefore, when you round this with truncate you get 119.09.
For what it is worth, you should always specify a length when converting to, or declaring a varchar
In Firebird DB: I read about the difference between numeric and decimal but still not sure Which is more suitable for prices calculations: decimal or numeric without rounding numbers?
The implementation for both NUMERIC and DECIMAL are almost identical in Firebird. The Interbase 6.0 Data Definition Guide says there is a subtle difference (for NUMERIC precision is exactly as declared, for DECIMAL the precision is at least equal to declared). According to The Firebird Book by Helen Borrie there is only a difference for numbers with a precision of less than 5 as a NUMERIC of precision 1-4 is mapped to a SMALLINT while a DECIMAL of precision 1-9 is mapped to INTEGER. This book also remarks that both NUMERIC and DECIMAL conform to the behavior of the SQL-92 type DECIMAL.
So with that said, I'd advise to go for DECIMAL, as its behaviour conforms to that defined in the SQL standards and therefor holds less surprise for people unfamiliar with Firebird, but who do understand the standard behaviour.
They're both equally suitable for price calculations. But there will always be certain kinds of calculations that require rounding, no matter which of these you use.
Decimal is the more flexible of the two.