Pergunta

According to MSDN, the range for REAL values is - 3.40E + 38 to -1.18E - 38, 0 and 1.18E - 38 to 3.40E + 38. However, I have quite a few values beyond that range in my table.

The following query returns lots of very small values and no very large ones:

SELECT  MyColumn ,
        *
FROM    data.MyTable
WHERE   MyColumn <> 0
        AND ( MyColumn < CONVERT(REAL, 1.18E-38)
              OR MyColumn > CONVERT(REAL, 3.40E+38)
            )
        AND ( MyColumn < CONVERT(REAL, -3.40E+38)
              OR MyColumn > CONVERT(REAL, -1.18E-38)
            ) 

It is easy to show how these values end up in the table. I cannot insert them directly:

CREATE TABLE a(r REAL NULL);
GO
INSERT INTO a(r) VALUES(4.330473E-39);
GO
SELECT r FROM a
GO
DROP TABLE a;

----
0.0

But I can divide two columns and get and outside of range value:

CREATE TABLE a
  (
    r1 REAL NULL ,
    r2 REAL NULL ,
    r3 REAL NULL
  ) ;
GO
INSERT  INTO a
        ( r1, r2 )
VALUES  ( 4.330473E-38, 1000 ) ;
GO
UPDATE  a
SET     r3 = r1 / r2 ;
SELECT  r1 ,
        r2 ,
        r3
FROM    a

r1            r2            r3
------------- ------------- -------------
4.330473E-38  1000          4.330433E-41

So I guess MSDN gives wrong ranges of valid data, correct? Am I missing anything?

Several people suggested that this is a bug.

What part of this behavior exactly is a bug. is it:

  1. Wrong constants documented in MSDN and used in DBCC, as well as wrong threshold for rounding down.
  2. Update being able to save wrong values
Foi útil?

Solução

Books Online documents only the normal range for single- and double-precision floating point numbers. The IEEE 754 rules also specify floating-point numbers closer to zero than the smallest non-zero normal value, known variously as denormalized, denormal, and subnormal numbers. From that last link:

Denormal numbers provide the guarantee that addition and subtraction of floating-point numbers never underflows; two nearby floating-point numbers always have a representable non-zero difference. Without gradual underflow, the subtraction a−b can underflow and produce zero even though the values are not equal. This can, in turn, lead to division by zero errors that cannot occur when gradual underflow is used.

SQL Server is following the rules for single-precision floating point calculations in the examples posted. The bug may be that DBCC checks only for normal values, and throws an incorrect error message when it encounters a stored denormal value.

Example producing a denormal single-precision value:

DECLARE
    @v1 real = 14e-39,
    @v2 real = 1e+07;

-- 1.4013e-045
SELECT @v1 / @v2;

Example showing a stored float denormal passes DBCC checks:

CREATE TABLE dbo.b (v1 float PRIMARY KEY);
INSERT b VALUES (POWER(2e0, -1075));
SELECT v1 FROM b; -- 4.94065645841247E-324
DBCC CHECKTABLE(b) WITH DATA_PURITY; -- No errors or warnings
DROP TABLE dbo.b;

Outras dicas

This is a bug in SQL Server. The last script you post is a nice repro. Add one line to it at the end:

DBCC CHECKDB WITH data_purity

This fails with:

Msg 2570, Level 16, State 3, Line 1 Page (1:313), slot 0 in object ID 357576312, index ID 0, partition ID 1801439851932155904, alloc unit ID 2017612634169999360 (type "In-row data"). Column "r3" value is out of range for data type "real". Update column to a legal value.

This proves it is a bug. I suggest you file a bug with Microsoft Connect for SQL Server.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top