Точность текста с плавающей запятой Python / numpy

Предположим, что у меня есть 32-битные и 64-битные значения с плавающей запятой:

>>> import numpy as np >>> v32 = np.array([5, 0.1, 2.4, 4.555555555555555, 12345678.92345678635], dtype=np.float32) >>> v64 = np.array([5, 0.1, 2.4, 4.555555555555555, 12345678.92345678635], dtype=np.float64) 

Я хочу сериализовать эти значения в тексте, не теряя точности (или, по крайней мере, на самом деле близкую к не потере точности). Я думаю, что канонический способ сделать это с repr :

 >>> map(repr, v32) ['5.0', '0.1', '2.4000001', '4.5555553', '12345679.0'] >>> map(repr, v64) ['5.0', '0.10000000000000001', '2.3999999999999999', '4.5555555555555554', '12345678.923456786'] 

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

 >>> ('%.7g ' * len(v32)) % tuple(v32) '5 0.1 2.4 4.555555 1.234568e+07 ' >>> ('%.16g ' * len(v32)) % tuple(v64) '5 0.1 2.4 4.555555555555555 12345678.92345679 ' 

Мой вопрос: безопасно ли использовать %g таким образом? Правильны ли значения .7 и .16 чтобы точность не терялась?

3 Solutions collect form web for “Точность текста с плавающей запятой Python / numpy”

Python 2.7 и более поздние repr уже имеют интеллектуальную реализацию для float, которая печатает 0,1 как 0.1 . Краткий вывод выбирается предпочтительнее других кандидатов, таких как 0.10000000000000001 потому что это кратчайшее представление этого конкретного числа, которое округляет до того же значения с плавающей запятой при чтении обратно на Python. Чтобы использовать этот алгоритм, конвертируйте свои 64-битные поплавки в реальные поплавки Python, прежде чем передавать их в repr :

 >>> map(repr, map(float, v64)) ['5.0', '0.1', '2.4', '4.555555555555555', '12345678.923456786'] 

Удивительно, но результат естественный и численно правильный. Более подробную информацию о версии 2.7 / 3.2 можно найти в « Что нового» и увлекательной лекции Марка Дикинсона.

К сожалению, этот трюк не будет работать для 32-битных поплавков, по крайней мере, не переосмысливая алгоритм, используемый в repr Python 2.7.

Чтобы однозначно определить число с плавающей запятой с одной точностью (32-разрядной) в формате IEEE-754, может потребоваться использовать 9 (значимое, т.е. не начинающееся с 0, если значение равно 0) десятичные цифры, а 9 цифр всегда достаточно.

Для чисел с плавающей запятой с двойной точностью (64 бит) может понадобиться 17 (значащих) десятичных цифр и всегда достаточны.

Я не совсем уверен, как определяется формат %g , он может позволить представлению начинаться с 0 (0,1), поэтому безопасные значения для точности будут .9 и .17 .

Если вы хотите минимизировать размер файла, запись байтовых представлений приведет к созданию гораздо меньшего файла, поэтому, если вы можете это сделать, это путь.

Код C, который реализует фантастическое представление в версии 2.7, в основном состоит из Python / dtoa.c (с обертками в Python / pystrtod.c и Objects / floatobject.c). В частности, посмотрите на _Py_dg_dtoa. Должна быть возможность заимствовать этот код и модифицировать его для работы с float вместо double. Затем вы можете обернуть это в модуль расширения или просто создать его так и ctypes.

Кроме того, обратите внимание, что источник говорит, что реализация «вдохновлена« Как печатать цифры с плавающей точкой »Томаном Ги Слилем-младшим и Джоном Л. Уайтом [Proc. ACM SIGPLAN '90, стр. 112-126] «. Таким образом, вы можете реализовать что-то менее гибкое и более простое, прочитав эту бумагу (и какой бы ни был modfications, описанный в комментариях dtoa.c, кажется уместным).

Наконец, код является незначительным изменением кода, представленным Дэвидом Гей в AT & T, и используется в ряде других библиотек (NSPR и т. Д.), Один из которых может иметь более доступную версию.

Но прежде чем делать что-либо из этого, убедитесь, что на самом деле проблема с производительностью, попробовав функцию Python и измеряя, слишком ли она слишком медленная.

И если это действительно критически важная для вас область, вы, вероятно, не захотите перебирать список и сначала вызвать функцию (или собственную собственную функцию C); вам, вероятно, нужна функция, которая преобразует массив numpy с плавающей точкой или удваивается в строку сразу. (В идеале вы хотели бы построить это в numpy, конечно.)

Последняя мысль: вы ищете «по крайней мере, очень близко, чтобы не потерять точность». Понятно, что просто преобразование в двойное и использование реферата достаточно близко для ваших целей, и это, очевидно, намного проще, чем что-либо еще, поэтому вы должны хотя бы протестировать его, чтобы его исключить.

Излишне говорить, что вы также должны проверить, достаточно ли %.9g и %.17g для ваших целей, поскольку это следующая самая легкая вещь, которая может работать.

  • Обнаружение, если массив NumPy содержит хотя бы одно нечисловое значение?
  • numpy.array файла изображения «I; 16»
  • Тест, если массив numpy содержит только нули
  • Python. Относится к кластеру k-к примеру.
  • Обновление numpy не выполняется с ошибкой «Permission denied»
  • Итерирование по массиву numpy
  • Возможно ли сопоставить несвязанные данные на диске с массивом с python?
  • Ненужная longdouble арифметика, похоже, не длинная двойная с конверсией
  • Python - лучший язык программирования в мире.