Я делаю географические расчеты, и в итоге получаю широту и долготу для хранения в Geography::Point
объекте.Широта и долгота могут иметь максимум 7 цифр (что также дает точность до 11 мм, что достаточно).
Проблема заключается в следующем: если значение поля не может быть правильно сохранено в Double
, MS SQL округляет до ближайшего возможного числа, но делает это путем добавления набора цифр.
=> например, 5.9395772
сохраняется как 5.9395771999999996
Проблема, которую это создает, заключается в том, что [Position].ToString()
превышает максимально допустимое количество символов для этого столбца (и нет, я не могу увеличить этот предел).
Поскольку мы имеем дело с Локатором,Долгота, Высота и Точность, есть место для ровно 11 символов для Широты и Долготы каждый:
String.Format(CultureInfo.InvariantCulture, "{0:##0.0######}", num)
Я пробовал просто Math.Round()
, используя 6 цифр, но затем другие цифры (например, 6.098163
до 6.0981629999999996
) получить ту же проблему.
Как мне Math.Round
по отношению к ближайшему 7-значному допустимому биту представление?
РЕДАКТИРОВАТЬ/ ADD
Public Function ToString_LatLon(ByVal num As Double) As String
num = Math.Round(num, 7, MidpointRounding.AwayFromZero)
Return String.Format(CultureInfo.InvariantCulture, "{0:##0.0######}", num)
End Function 'IN = 5.9395772, OUT = 5.9395772
Приведенный выше код получает Double
and правильно возвращает представление String
.Я проверил это, это верно и для тревожных чисел.
Он хранится в SQL Server через структуру, которую мы используем. Я думаю, что проблема возникает при сохранении значения
Когда я получаю значение, я получаю ошибку в VB, говоря, что значение шире, чем позволяет каркас (не более 50 символов).
Если я запускаю запрос в SSMS, я нахожу, например, POINT (X.0981629999999996 XX.664725 NULL 15602.707)
(51 символ, анимированный).
РЕДАКТИРОВАТЬ 2
Я провел еще несколько исследований и некоторые вычисления.Кажется, что сохраненное значение 5.9395772
преобразуется в двоичный файл и возвращается как 5.9395771999999996
, который хранится в виде двойного в базе данных (в двоичном объекте Geography::Point
, не беспокойтесь.) Преобразование двоичного файла 0 10000000001 0111110000100010000010000110100010000100010011011101
обратнодо десятичного знака, и вы получите 5.93957719999999955717839839053340256214141845703125
, но сокращенно до 16 десятичных знаков - тогда как я хотел бы, чтобы оно сокращалось до 7 десятичных знаков.
Решения:
- Округление значения вниз / вверх доближайшее значение, при котором все, начиная с 8-го знака после запятой, равно 0 (или достаточно нулей, прежде чем будет найдена другая ненулевая цифра)
- Запрос только о таком количестве десятичных знаков.
- Запрос фактического (шестнадцатеричного) значения,и преобразуйте это (вместо строкового представления)
- Сохраните строковое представление, но округлите значения перед сохранением и после получения до необходимого количества десятичных знаков.
Обсуждения:
- И в офисе, и у нас (ответ @ RobertBaron): это довольно сложно, может привести к значительному снижению точности и, в основном, много работы.
- Возможно, это возможно, я не знаю.
- Это было бы самое чистое решение, как и мои коллеги, и я согласен, однако это много работы вразработка и тестирование.
- Вместо того, чтобы заботиться о том, чтобы значение в памяти было равно значению в базе данных, нас не заботит значение в базе данных (слишком много).
В итоге, после нескольких вычислений на доске и долгого обсуждения, мы перешли к варианту 4 .После того, как мы извлекли [Position].ToString()
(для которого мы увеличили предел строки) из базы данных, мы конвертируем это, как мы уже делаем, и в качестве дополнительного шага перед его использованием в любом месте округляем значение до необходимого количества десятичных дробей,Возвращая значение в базу данных, мы снова округляем значение до количества десятичных дробей, и нам все равно, что на самом деле делает с ним база данных.
По сути, это вариант 2, но затем на стороне программы.вместо базы данных.