I'm storing a value (0.15) as a Real datatype in a Quantity field in SQL.
Just playing around, when I cast as numeric, there are some very slight changes to scale.
I'm unsure why this occurs, and why these particular numbers?
select CAST(Quantity AS numeric(18,18)) -- Quantity being 0.15
returns
0.150000005960464480
Real and float are approximate numerics, not exact ones. If you need exact ones, use DECIMAL.
The benefit of the estimated ones is that they allow storing very large numbers using fewer storage bytes.
https://learn.microsoft.com/en-us/sql/t-sql/data-types/float-and-real-transact-sql?view=sql-server-2017
PS:Numeric and decimal are synonymous.
PS2: See Eric's Postpischil clarification comment below:
"Float and real represent a number as a significand multiplied by a power of two. decimal represents a number as a significand multiplied by a power of ten. Both means of representation are incapable of representing all real numbers, and both means of representation are subject to rounding errors. As I wrote, dividing 1 by 3 in a decimal format will have a rounding error"
Related
Quick Aside: I'm going to use the word "Float" to refer to both a .Net float and a SQL float with only 7 significant digits. I will use the word "Double" to refer to a .Net double and a SQL float with 15 significant digits. I also realize that this is very similar to some other posts regarding decimals/doubles, but the answers on those posts are really inconsistent, and I really want some recommendations for my specific circumstance...
I am part of a team that is rewriting an old application. The original app used floats (7 digits). This of course caused issues since the app conducted a lot of calculations and rounding errors accumulated very quickly. At some point, many of these floats were changed to decimals. Later, the floats (7) in the database all became doubles (15). After that we had several more errors with calculations involving doubles, and they too were changed to decimals.
Today about 1/3 of all of our floating point numbers in the database are decimals, the rest are doubles. My team wants to "standardize" all of our floating-point numbers in the database (and the new .Net code) to use either exclusively decimals or doubles except in cases where the other MUST be used. The majority of the team is set on using decimals; I'm the only person on my team advocating using doubles instead of decimals. Here's why...
Most of the numbers in the database are still doubles (though much of the application code still uses floats), and it would be a lot more effort to change all of the floats/doubles to decimals
For our app, none of the fields stored are "exact" decimal quantities. None of them are monetary quantities, and most represent some sort of "natural" measurement (e.g. mass, length, volume, etc.), so a double's 16 significant digits are already way more precise than even our initial measurements.
Many tables have measurements stored in two columns: 1 for the value; 1 for the unit of measure. This can lead to a HUGE difference in scale between the values in a single column. For example, one column can store a value in terms of pCi/g or Ci/m3 (1 Ci = 1000000000000 pCi). Since all the values in a single decimal columns must have the same scale (that is... an allocated number of digits both before and after the decimal point), I'm concerned that we will have overflow and rounding issues.
My teammates argue that:
Doubles are not as accurate nor as precise as decimals due to their inability to exactly represent 1/10 and that they only have 16 significant digits.
Even though we are not tracking money, the app is a inventory system that keeps track of material (mostly gram quantities) and it needs to be "as accurate as possible".
Even after the floats were changed to doubles, we continued to have bad results from calculations that used doubles. Changing these columns (and the application code) to decimals caused these calculations to produce the expected results.
It is my strong belief that the original issues where caused due to floats only having 7 significant digits and that simple arithmetic (e.g. 10001 * 10001) caused them to the data to quickly use up the few significant digits that they had. I do not believe this had anything to do with how binary-floating point numbers can only approximate decimal values, and I believe that using doubles would have fixed this issue.
I believe that the issue with doubles arose because doubles were used along side decimals in calculations that values were be converted back and forth between data types. Many of these calculations would round between intermediary steps in the calculation!
I'm trying to convince my team not to make everything under the sun into a decimal. Most values in the database don't have more than 5 or 6 significant digits anyway. Unfortunately, I am out-ranked by other members of my team that see things rather differently.
So, my question then is...
Am I worrying over nothing? Is there any real harm done by using almost exclusively decimals instead of doubles in an application with nearly 200 database tables, hundreds of transactions, and a rewrite schedule of 5 to 6 years?
Is using decimals actually solving an issue that doubles could not? From my research, both decimals and doubles are susceptible to rounding errors involving arbitrary fractions (adding 1/3 for example) and that the only way to account for this is to consider any value within a certain tolerance as being "equal" when comparing doubles and/or decimals.
If it is more appropriate to use doubles, what arguments could I make (other than what I have already made) could convince my team to not change everything to decimals?
Use decimal when you need perfect accuracy as a base-10 number (financial data, grades)
Use double or float when you are storing naturally imprecise data (measurements, temperature), want much faster mathematical operations, and can sacrifice a minute amount of imprecision.
Since you seem to be only storing various measurements (which have some precision anyways), float would be the logical choice (or double if you need more than 7 digits of precision).
Is using decimals actually solving an issue that doubles could not?
Not really - The data is only going to be as accurate as the measurements used to generate the data. Can you really say that a measured quantity is 123.4567 grams? Does the equipment used to measure it have that level of precision?
To deal with "rounding errors" I would argue that you can't really say whether a measurement of 1234.5 grams is exactly halfway - it could just as easily be 1234.49 grams, which would round down anyways.
What you need to decide is "what level of precision is acceptable" and always round to that precision as a last step. Don't round your data or intermediate calculations.
If it is more appropriate to use doubles, what arguments could I make (other than what I have already made) could convince my team to not change everything to decimals?
Other than the time spent switching, the only thing you're really sacrificing is speed. The only way to know how much speed is to try it both ways and measure the difference.
You'd better try your best not to lose precision. I guess my fault may convince you to choose double.
===> I did some wrong arithmetic, and it returns something very weird:
given 0.60, it returns 5
int get_index(double value) {
if (value < 0 || value > 1.00)
return -1;
return value / 0.10;
}
and I fixed it:
int get_index(double value) {
if (value < 0 || value > 1.00)
return -1;
return (value * 100000000) / (0.10 * 100000000);
}
Say I have test results values for a lab procedure that come in as 103. What would be the best way to store this in SQL Server? I would think since this is numerical data it would be improper to just store it as string text and then program around calculating the data value from the string.
If you want to use your data in numeric calculations, it is probably best to represent your data using once of SQL servers native numeric data type. Since you show scientific notation, it is likely you will want to use either REAL or FLOAT.
Real is basically 7 decimal digits of precision and float has 15 digits of precision (at least this is how they are normally used). You can actually specify reduced precision for FLOAT, but in practice most people just use REAL in that case. REAL takes 4 bytes of storage, and FLOAT requires 8 bytes.
The other numeric types are for fixed decimal point arithmetic.
Numbers in scientific notation like this have three pieces of information:
The significand
The precision of the significand
The exponent of 10
Presuming we want to keep all this information as exact as possible, it may be best to store these in three non-floating point columns (floating-point values are inexact):
DECIMAL significand
INT precision (# of decimal places)
INT exponent
The downside to the approach of separating these parts out, of course, is that you'll have to put the values back together when doing calculations -- but by doing that you'll know the correct number of significant figures for the result. Storing these three parts will also take up 25 bytes per value (17 for the DECIMAL, and 4 each for the two INTs), which may be a concern if you're storing a very large quantity of values.
Update per explanatory comments:
Given that your goal is to store an exponent from 1-8, you really only need to store the exponent, since you know the mantissa is always 10. Therefore, if your value is always going to be a whole number, you can just use a single INT column; if it will have decimal places, you can use a FLOAT or REAL per Gary Walker, or use a DECIMAL to store a precise decimal to a specified number of places.
If you specify a DECIMAL, you can provide two arguments in the column type; the first is the total number of digits to be stored, while the second is the number of digits to the right of the decimal point. So if your values are going to be accurate to the tenths place, you might create a column of DECIMAL(2,1). SQL Server MSDN documentation: DECIMAL and NUMERIC types
What is the most appropriate data type for storing currency values in VB.NET?
Decimal (alias for System.Decimal structure in the BCL) is designed for storing monetary values. It's a 128 bit decimal floating point type (as opposed to binary floating point) and is useful for storing "real-world" values with high decimal precision. By real-world, I specifically mean measurements that are originally made in decimal. Double is generally suitable for calculations that don't need as much accuracy when they are represented as decimal numbers.
The Decimal value type represents decimal numbers ranging from positive 79,228,162,514,264,337,593,543,950,335 to negative 79,228,162,514,264,337,593,543,950,335. The Decimal value type is appropriate for financial calculations requiring large numbers of significant integral and fractional digits and no round-off errors. The Decimal type does not eliminate the need for rounding. Rather, it minimizes errors due to rounding. For example, the following code produces a result of 0.9999999999999999999999999999 rather than 1.
What are the differences between numeric, float and decimal datatypes and which should be used in which situations?
For any kind of financial transaction (e.g. for salary field), which one is preferred and why?
use the float or real data types only if the precision provided by decimal (up to 38 digits) is insufficient
Approximate numeric data types(see table 3.3) do not store the exact values specified for many numbers; they store an extremely close approximation of the value. (Technet)
Avoid using float or real columns in WHERE clause search conditions, especially the = and <> operators. It is best to limit float and real columns to > or < comparisons. (Technet)
so generally choosing Decimal as your data type is the best bet if
your number can fit in it. Decimal precision is 10E38[~ 38 digits]
smaller storage space (and maybe calculation speed) of Float is not important for you
exact numeric behavior is required, such as in financial applications, in operations involving rounding, or in equality checks. (Technet)
Exact Numeric Data Types decimal and numeric - MSDN
numeric = decimal (5 to 17 bytes)
will map to Decimal in .NET
both have (18, 0) as default (precision,scale) parameters in SQL server
scale = maximum number of decimal digits that can be stored to the right of the decimal point.
money(8 byte) and smallmoney(4 byte) are also Exact Data Type and will map to Decimal in .NET and have 4 decimal points (MSDN)
Approximate Numeric Data Types float and real - MSDN
real (4 byte)
will map to Single in .NET
The ISO synonym for real is float(24)
float (8 byte)
will map to Double in .NET
All exact numeric types always produce the same result, regardless of which kind of processor architecture is being used or the magnitude of the numbers
The parameter supplied to the float data type defines the number of bits that are used to store the mantissa of the floating point number.
Approximate Numeric Data Type usually uses less storage and have better speed (up to 20x) and you should also consider when they got converted in .NET
What is the difference between Decimal, Float and Double in C#
Decimal vs Double Speed
SQL Server - .NET Data Type Mappings (From MSDN)
main source : MCTS Self-Paced Training Kit (Exam 70-433): Microsoft® SQL Server® 2008 Database Development - Chapter 3 - Tables, Data Types, and Declarative Data Integrity Lesson 1 - Choosing Data Types (Guidelines) - Page 93
Guidelines from MSDN: Using decimal, float, and real Data
The default maximum precision of numeric and decimal data types is 38.
In Transact-SQL, numeric is functionally equivalent to the decimal
data type. Use the decimal data type to store numbers with decimals
when the data values must be stored exactly as specified.
The behavior of float and real follows the
IEEE 754 specification on approximate numeric data types. Because of the approximate nature of the float and real data types, do not use these data types when exact
numeric behavior is required, such as in financial applications, in
operations involving rounding, or in equality checks. Instead, use the
integer, decimal, money, or smallmoney data types. Avoid using float
or real columns in WHERE clause search conditions, especially the =
and <> operators. It is best to limit float and real columns to > or <
comparisons.
They Differ in Data Type Precedence
Decimal and Numeric are the same functionally but there is still data type precedence, which can be crucial in some cases.
SELECT SQL_VARIANT_PROPERTY(CAST(1 AS NUMERIC) + CAST(1 AS DECIMAL),'basetype')
The resulting data type is numeric because it takes data type precedence.
Exhaustive list of data types by precedence:
Reference link
Not a complete answer, but a useful link:
"I frequently do calculations against decimal values. In some cases casting decimal values to float ASAP, prior to any calculations, yields better accuracy. "
http://sqlblog.com/blogs/alexander_kuznetsov/archive/2008/12/20/for-better-precision-cast-decimals-before-calculations.aspx
The case for Decimal
What it the underlying need?
It arises from the fact that, ultimately, computers represent, internally, numbers in binary format. That leads, inevitably, to rounding errors.
Consider this:
0.1 (decimal, or "base 10") = .00011001100110011... (binary, or "base 2")
The above ellipsis [...] means 'infinite'. If you look at it carefully, there is an infinite repeating pattern (='0011')
So, at some point the computer has to round that value. This leads to accumulation errors deriving from the repeated use of numbers that are inexactly stored.
Say that you want to store financial amounts (which are numbers that may have a fractional part). First of all, you cannot use integers obviously (integers don't have a fractional part).
From a purely mathematical point of view, the natural tendency would be to use a float. But, in a computer, floats have the part of a number that is located after a decimal point - the "mantissa" - limited. That leads to rounding errors.
To overcome this, computers offer specific datatypes that limit the binary rounding error in computers for decimal numbers. These are the data type that
should absolutely be used to represent financial amounts. These data types typically go by the name of Decimal. That's the case in C#, for example. Or, DECIMAL in most databases.
Float is Approximate-number data type, which means that not all values in the data type range can be represented exactly.
Decimal/Numeric is Fixed-Precision data type, which means that all the values in the data type range can be represented exactly with precision and scale. You can use decimal for money saving.
Converting from Decimal or Numeric to float can cause some loss of precision. For the Decimal or Numeric data types, SQL Server considers each specific combination of precision and scale as a different data type. DECIMAL(2,2) and DECIMAL(2,4) are different data types. This means that 11.22 and 11.2222 are different types though this is not the case for float. For FLOAT(6) 11.22 and 11.2222 are same data types.
You can also use money data type for saving money. This is native data type with 4 digit precision for money. Most experts prefers this data type for saving money.
Reference
1
2
3
Decimal has a fixed precision while float has variable precision.
EDIT (failed to read entire question):
Float(53) (aka real) is a double-precision (64-bit) floating point number in SQL Server. Regular Float is a single-precision (32-bit) floating point number. Double is a good combination of precision and simplicty for a lot of calculations. You can create a very high precision number with decimal -- up to 136-bit -- but you also have to be careful that you define your precision and scale correctly so that it can contain all your intermediate calculations to the necessary number of digits.
Although the question didn't include the MONEY data type some people coming across this thread might be tempted to use the MONEY data type for financial calculations.
Be wary of the MONEY data type, it's of limited precision.
There is a lot of good information about it in the answers to this Stackoverflow question:
Should you choose the MONEY or DECIMAL(x,y) datatypes in SQL Server?
What is the diff between real, float, decimal and money. And most important, when would I use them. Like I understand - real and float are approx. types, meaning they dont store the exact value. Why would you ever want this?
Thanks
real and float numeric types are useful to handle a very wide range of values as is encountered with physical dimensions or mathematical results.
The loss of precision they incur, for example when adding values which are not in the same range, for example 0.00002468 + 1.23E9 (i.e. 1,230,000) is typically acceptable for practical uses. This is a small tribute to pay to the relatively compact storage requirements of these floating point types.
The decimal and money types do not cover such a broad range (yet they cover ranges that are beyond most typical accounting applications), and do not exhibit any of this lossy behavior with rounding and such.
See MS-SQL document for detailed information. The following table provides an indicative precision, range and storage requirement for various types.
Type Max value precision(*) Storage
money +/-922,000,000,000,000 3 (4?) 8 bytes
smallmoney +/-200,000 3? 4 bytes
decimal varies (as defined) varies varies 3 to 17
real +/- 3.4 * 10^38 7 digits 4 bytes
float "56" +/- 1.7 * 10 ^308 15 digits 8 bytes (float can also be declared to be just like a real)
(*) precision : For the "exact" types, this is the number of digits after the decimal point. For the "lossy" reals and floats, this is the number of significant digits.
Money is an exact data type. as in it is continuous between its upper and lower bound. You would generally use it when you want to store values of money and don't want to lose precision and get rounding errors caused by IEEE754. Decimal is a similarly an exact data type that isn't lossy up to a certain number of decimal places (which you can specify). Real is equivalent to float(24).
To be clear, precision loss can still occur when using division, but all other basic mathematical operations do not incur precision loss for Money and decimal types.
See here for an explanation of the various Transact SQL data types.