SQLite3 на Android неправильно добавляет числа с плавающей запятой

У меня возникли проблемы с запуском нескольких простых операторов SQLite3 на Android.

Например:

SELECT 1234 + 0.001

Это должно вернуть 1234.001, верно? Однако SQLite возвращает 1234 как на эмуляторе (v21), так и на реальном устройстве (v19).

Учитывая, что 1234 хранится в столбце с именем Field1, тип REAL, в Table1, я пробовал все варианты ниже:

SELECT Field1 + 0.001 FROM Table1

SELECT (Field1 * 1.000)  + 0.001 FROM Table1

SELECT CAST(Field1 as FLOAT) + 0.001 FROM Table1

SELECT CAST(Field1 as REAL) + 0.001 FROM Table1

SELECT Field1 + 0.001 from Table1

SELECT CAST((Field1 + 0.001) as REAL) FROM Table1

Кажется, ничего не работает, и в каждом отдельном случае я получаю 1234 вместо 1234.001. Мне нужно получить 1234.001 из 1234 в запросе, но SQLite3 не помогает.

Еще одна вещь, которую я только что обнаружил: если "Field1" ‹= 999, SELECT работает, как и ожидалось, и в результате я получаю 999,001. Все, что >= 1000, вместо этого дает мне целое число.

Не могли бы вы помочь мне, как это решить?

введите здесь описание изображения


person AlxDroidDev    schedule 18.07.2016    source источник
comment
Это должно вернуть 1234.001, верно? -- не обязательно. 1234.001 не может быть представлено как число с плавающей запятой.   -  person CommonsWare    schedule 18.07.2016
comment
Вероятно, вы получаете данные как целое число. Пока вам нужен плавающий или двойной.   -  person Phantômaxx    schedule 18.07.2016
comment
@CommonsWare согласился, но ни одно из чисел ›= 1000 не возвращается как x.001, как показано на снимке экрана выше. С другой стороны, любое число ‹ 1000 возвращается, как и ожидалось.   -  person AlxDroidDev    schedule 18.07.2016
comment
Как именно вы форматируете числа в своем коде?   -  person CL.    schedule 18.07.2016
comment
Я не форматировал номер. Я читал их с cursor.getString(), и я думаю, что это было виновником.   -  person AlxDroidDev    schedule 19.07.2016


Ответы (2)


На самом деле, я обнаружил, что проблема не в том, чем кажется. Числа обрабатываются как x.001, но не отображаются как таковые. Внутренне числа правильно сохранялись в двоичном формате, но отображалась не вся десятичная часть. Просто приведя их к тексту, я смог увидеть, что там была вся десятичная часть.

В любом случае, спасибо за ваше время и ответы.

введите здесь описание изображения

person AlxDroidDev    schedule 18.07.2016

Это вопрос точности и того, как числа хранятся в компьютере или, если на то пошло, в sqlite3. Числа хранятся в двоичном формате, что означает, что при преобразовании десятичного числа в двоичное число вы теряете некоторую точность (в действительных числах) подробнее здесь. Теперь, каковы ваши варианты, если вы хотите, чтобы сложение дало 1234,001, как в вашем примере?

A) Сохранение числа в виде строки.

Вы всегда можете сохранить значения «1234» и «0,001» как VARCHAR, а в коде JAVA проанализировать эти значения в BigDecimals и выполнить там свои добавления. Для получения дополнительной информации о синтаксическом анализе перейдите по этой ссылке. Недостатком этого метода является то, что он будет потреблять гораздо больше места для хранения в вашей базе данных, а операции синтаксического анализа также не будут такими быстрыми. Прежде чем использовать этот метод, подумайте, не повлияет ли этот недостаток на производительность вашего приложения.

B) Установите MAXIMUM_PRECISION и сохраните их как INTEGER.

По умолчанию SQLITE3 хранит INTEGER, используя 8 байтов. Это означает, что наибольшее целое число, которое вы можете сохранить, имеет порядок 10^19. Теперь, сохраняя целые числа, вы не теряете точность; так что мы можем воспользоваться этим. Предположим, что ваша MAXIMUM_PRECISION равна 0,001 (как в вашем примере), тогда вам нужно умножить это число на одну тысячу, чтобы получить 1. А что, если вместо представления 1234,001 как реального числа мы представим его как 1234001 int? таким образом мы можем безопасно сохранить int в sqlite3 и убедиться, что операции работают правильно. В своем коде вы можете позже проанализировать число как строку и отформатировать его, чтобы отобразить его пользователю, или вы можете проанализировать его до BigDecimal, чтобы сохранить точность. Конечно, это ограничит вас максимальным числом порядка 10^16; еще раз проверьте свои требования, чтобы увидеть, сработает ли этот трюк для вашего приложения. Обратите внимание, что аналогичный трюк используется для хранения валюты без потери точности в sqlit3, для получения дополнительной информации см.: эта ссылка

person Gerardo Teruel    schedule 18.07.2016