Безопасно ли хранить точную позицию звука в виде секунд в double?

#java #c #sqlite #audio #floating-point

#java #c #sqlite #Аудио #значение с плавающей запятой

Вопрос:

Мне нужно сохранить точную позицию звука в базе данных, а именно SQLite. Я мог бы сохранить позицию кадра (смещение выборки / каналы) как целое число, но это привело бы к дополнительному обслуживанию данных в случае определенных преобразований файлов.

Итак, я подумываю о сохранении позиции в виде 8-байтового реального значения в секундах, то есть double, и, следовательно, как РЕАЛЬНОГО в SQLite. Это делает структуру базы данных более согласованной.

Но, учитывая максимальную частоту дискретизации 192 кГц, достаточна ли двойная точность, чтобы я всегда мог восстановить точную позицию кадра при умножении значения на частоту дискретизации?

Существует ли определенное максимальное положение, выше которого может возникнуть ошибка? Какова эта максимальная позиция?

PS: речь идет о SQLite REAL, но также о типах C и Java double, которые могут содержать значение позиции на разных этапах.

Обновить:

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

 // Given these types:
int samplerate;
long long framepos;
double position;

// First compute the position in seconds from the framepos:
position = (double) framepos / samplerate;

// Now store the position in an SQLite REAL column, and retrieve it later

// Then compute the framepos back from position, with rounding:
framepos = position * samplerate   0.5;
  

Безопасно ли это и симметрично ли?

Ответ №1:

Точность double составляет 51 бит. В зависимости от части экспоненты, некоторые из этих битов будут представлять целые числа (секунды в вашем случае), другие доли секунды. При 48 килобитах требуется минимум 16 бит, чтобы получить достаточно точную подсекунду (больше, если округление не является оптимальным). Это оставляет 35 бит для секунд, которые будут занимать чуть более тысячи лет.

Таким образом, даже если вам нужен дополнительный бит или два для подсекунды, чтобы защититься от округления, и даже если SQL теряет бит или два точности при преобразовании его в десятичное число и обратно здесь и там, вы и близко не потеряете точность выборки с вашим номером двойной точности. Убедитесь, что ваше округление работает правильно — C имеет тенденцию всегда округлять в меньшую сторону при преобразовании в целое число, поэтому даже незначительная ошибка преобразования может сбить вас с толку на 1.

Комментарии:

1. Частота дискретизации указана в 192 кГц, так что это 18 бит. При каждом преобразовании теряется 1 бит, так что это как минимум на 2 бита меньше (1 при сохранении и 1 при извлечении). Но оставшиеся 31 бит все равно оставляют вас в масштабе ста лет.

2. Разве это не 53 (52 1 неявный) бита точности?

3. Спасибо, ребята, звучит безопасно! И спасибо за упоминание округления, я обращу на это внимание. IIUC, риск больше связан с преобразованием, чем с хранением.

4. Как сказал Бен: IEEE-754 double имеет точность 53 бита.

5. подходит ли вам метод преобразования и округления, который я только что добавил к своему вопросу?

Ответ №2:

Я бы сохранил ее как (64-разрядное) целое число, представляющее микросекунды (приблизительно 2 * * 20). Это позволяет избежать аппаратного / программного обеспечения с плавающей запятой, легко понимается всеми и дает вам диапазон 0 .. 2 ** 44 секунды, что составляет чуть более 55 тысяч лет.

В качестве альтернативы используйте читаемое десятичное представление с фиксированной точностью (20 цифр должно быть достаточно). Выравнивание по правому краю с начальными нулями. Стоимость преобразования в любом случае незначительна по сравнению с доступом к БД.

Одним из преимуществ этих опций является то, что любая база данных будет тривиально знать, как их упорядочить, не обязательно очевидно для значений с плавающей запятой.

Комментарии:

1. Хм, при таком подходе не нужно ли оставлять около 18 бит свободным, чтобы избежать переполнения целых чисел при выполнении pos * samplerate / 10 ^ 6? Или вы советуете использовать метод double в этой операции?

2. Хм, да, при этом вы получаете на 18 бит (или около того) меньше, чем 44 бита, которые, как я думал, вы получите с диапазоном. Тогда вы можете использовать время только до 72 лет или около того. Неприятно 🙂

3. Нет, это всего лишь 2 года 😉 2^26 / 3600 / 24 / 365.25 = 2.1266 ; и с целым числом со знаком, равным 1 году. Этого, конечно, достаточно. Но я думаю, что также могут возникнуть проблемы с округлением. Округление чистого целого числа подвержено ошибкам. Удобнее использовать двойные значения повсеместно, IMO. И использование секунд для определения позиции / длительности очень естественно.

4. @oliverg: Мои расчеты оказались неверными 🙂 спасибо. Но в любом случае ваше утверждение о том, что «округление чистого целого числа подвержено ошибкам», трудно принять. Округление с фиксированной точностью предсказуемо и не зависит от величины округленного значения. В арифметике с плавающей запятой есть много-много подводных камней (для одного результата NaN), и вам следует избегать этого, если только это не является строго необходимым и вы не готовы провести тщательный анализ рисков и ошибок.

5. возможно, я слишком быстро написал о целочисленном округлении. Но как могли упомянутые преобразования C завершиться неудачей? (смотрите Обновление в моем вопросе)

Ответ №3:

Как объясняется в ответе Маттиаса Ванделя, вероятно, беспокоиться не о чем. OTOH используя целые числа, вы получите фиксированную точность независимо от величины, которая может быть полезна.

Скажем, используйте 64-разрядное целое число и храните время в виде микросекунд. Это дает вам эквивалентную точность дискретизации в 1 МГц и диапазон почти в 300000 лет (если мой быстрый расчет верен).

Редактировать Даже если принять во внимание необходимость того, чтобы временная метка * sample_rate помещалась в 64-разрядное целое число, у вас все еще есть диапазон в 1,5 года (2**63/ 1e6/3600/24/365/ 192e3), предполагая максимальную частоту дискретизации 192 кГц.

Комментарии:

1. пожалуйста, посмотрите мой комментарий к ответу Стива

2. @olivierg: Хорошая мысль; Я соответствующим образом обновил свой ответ. Тем не менее, беспокоиться по-прежнему не о чем.