TSQL - сделать буквальное значение с плавающей точкой - PullRequest
5 голосов
/ 11 июня 2010

Я понимаю множество проблем при сравнении поплавков и оплакиваю их использование в этом случае - но я не являюсь автором таблицы и у меня есть только небольшое препятствие для подъема ...

Кто-то решилиспользуйте float, так как вы ожидаете использовать GUID.Мне нужно извлечь все записи с определенным значением с плавающей запятой.

sp_help MyTable

-- Column_name  Type    Computed    Length  Prec
-- RandomGrouping   float   no  8   53   

Вот моя наивная попытка:

--yields no results
SELECT RandomGrouping
FROM MyTable
WHERE RandomGrouping = 0.867153569942739

И вот примерно рабочая попытка:

--yields 2 records
SELECT RandomGrouping
FROM MyTable
WHERE RandomGrouping BETWEEN 0.867153569942739 - 0.00000001
      AND 0.867153569942739 + 0.00000001

--  0.867153569942739
--  0.867153569942739

В моей наивной попытке этот литерал является литералом с плавающей точкой?Или это действительно десятичный литерал, который конвертируется позже?

Если мой литерал не является литералом с плавающей запятой, каков синтаксис для создания литерала с плавающей запятой?

РЕДАКТИРОВАТЬ: Другая возможность имеетмне пришло в голову ... может быть, в этом столбце хранится более точное число, чем отображается.Может быть невозможно создать литерал, который представляет это число.Я приму ответы, которые демонстрируют, что это так.


РЕДАКТИРОВАТЬ: ответ на DVK.

TSQL - это диалект SQL MSSQLServer.

Этот скрипт работает,и поэтому равенство может быть выполнено детерминистически между типами с плавающей точкой:

DECLARE @X float
SELECT top 1 @X = RandomGrouping
FROM MyTable
WHERE RandomGrouping BETWEEN 0.839110948199148 - 0.000000000001
  AND 0.839110948199148 + 0.000000000001
  --yields two records
SELECT *
FROM MyTable
WHERE RandomGrouping = @X

Я сказал «приблизительно», потому что этот метод проверяет диапазон.С помощью этого метода я мог получить значения, которые не равны моему предполагаемому значению.

Связанная статья не применима, потому что я (намеренно) не пытаюсь перешагнуть границы мира между десятичной дробью и плавающей точкой.Я пытаюсь работать только с поплавками.Речь идет не о неконвертируемости десятичных чисел к числам с плавающей запятой.


Ответ на Zinglon:

Буквальное значение, которое может найти мои записи, спасибо.

DECLARE @Y binary(8)
SET @Y = 0x3FEAD9FF34076378

SELECT *
FROM MyTable
WHERE convert(binary(8), RandomGrouping) = @Y

Ответы [ 3 ]

1 голос
/ 11 июня 2010

Возможно, что значения усекаются на дисплее. Я предполагаю, что столбец не имеет уникальных ограничений, иначе вопрос будет спорным. На моей установке SSMS усекает более точное значение в этом сценарии.

create table flt ( f float not null primary key )
insert into flt
select 0.111111111111111
union all
select 0.1111111111111111
select f, cast(f as binary(8)) from flt

Аналогичным образом, если эти значения различны, вы можете привести их к двоичному (8) и идентифицировать их на основе этого значения, например:

select f from flt
where cast(f as binary(8)) = 0x3FBC71C71C71C71C
1 голос
/ 04 марта 2019

это литерал литерал с плавающей точкой? Или это действительно десятичная дробь литерал, который конвертируется позже?

Если мой литерал не является литералом с плавающей запятой, каков синтаксис для сделать литерал с плавающей точкой?

Литерал 0.867153569942739 в SQL Server имеет тип decimal, а не float. Движок автоматически выбирает соответствующий масштаб и точность для представления заданного литерала.

Чтобы написать литерал типа float, вы должны использовать научную нотацию, например: 0.867153569942739E0

Это задокументировано в Константах (Transact-SQL)

десятичные константы

десятичные константы представлены строкой числа, которые не заключены в кавычки и содержат десятичную дробь точка. Ниже приведены примеры десятичных констант:

1894.1204  
2.0  

float и реальные константы

float и real константы представлены как используя научную запись. Ниже приведены примеры значений float или real :

101.5E5  
0.5E-2

sp_describe_first_result_set может сообщить нам типы столбцов

EXEC sp_describe_first_result_set N'SELECT 0.867153569942739, 0.867153569942739E0'

Возвращает numeric(15,15) для первого столбца и float для второго.


Если ваш столбец RandomGrouping проиндексирован, гораздо эффективнее использовать литерал float, поскольку при переносе RandomGrouping в convert() индекс не может использоваться.

В следующем запросе будет использоваться индекс:

SELECT *
FROM MyTable
WHERE RandomGrouping = 0.867153569942739E0

Следующий запрос не будет использовать индекс:

SELECT *
FROM MyTable
WHERE convert(binary(8), RandomGrouping) = @Y
0 голосов
/ 11 июня 2010

Проблема не в том, является ли это литералом с плавающей запятой или нет.

Проблема в том, что сравнение двух чисел с плавающей точкой на равенство в Sybase (или на любом сервере БД) не является детерминированным, поскольку 4.00000000000000000000 ... и 3.99999999999999999999 ... имеют одинаковое точное число, но не равны.

Ваше второе решение - единственный правильный способ сравнить числа с плавающей точкой на «равенство» (т. Е. Одинаковы ли они с точностью до точности).

Почему вы говорите "приблизительно работаете" по поводу вашего второго подхода?

Поскольку вы не указали конкретный сервер БД, который вы используете, вот довольно приличное описание проблемы (с теми же выводами, что и выше) для MySQL

http://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html

...