Я скопировал этот стол из википедии в базу данных PostgreSQL. Колонка Cultivated land (km2)
стал столбцом типа real
, Затем я использую команду PHP
echo rtrim(rtrim(sprintf('%.10F',$v),'0'),'.');
отображать цифры ($v
) в таблице (как целые, так и с плавающей точкой), но некоторые значения теряют точность. Например, значение из США, 1669302, становится 1669300, что странно, так как Я ожидал 10 десятичных цифр точности. Я думал, что потерял точность при сохранении в real
столбец, но преобразование столбца в double precision
делает разницу (02) появляется снова, так что это было где-то там.
Я не думаю, что мне нужна двойная точность, так как я могу правильно отобразить реальное значение? Имейте в виду, что некоторые столбцы имеют десятичные разряды, а другие bigint
и они также должны отображаться правильно.
Кажется, проблема возникает из-за того, как PHP возвращает результаты. Значения не возвращаются как соответствующий тип данных, а скорее отформатирован как строка с использованием стандартного форматирования PostgreSQL. Это форматирование, отличается для real
а также double precision
Таким образом, при преобразовании типов столбцов таблицы вы видите разные результаты. Причина, по которой вы видите этот конкретный результат, заключается в том, что PostgreSQL гарантирует 6 десятичных знаков для real
типы и 15 знаков после запятой для double precision
.
extra_float_digits
Руководство состояния
Обратите внимание
extra_float_digits
Параметр setting определяет количество дополнительных значащих цифр, включаемых при преобразовании значения с плавающей запятой в текст для вывода. Со значением по умолчанию0
выходные данные одинаковы для всех платформ, поддерживаемых PostgreSQL. Увеличение его приведет к выводу, который более точно представляет сохраненное значение, но может быть непереносимым.
Поэтому простым решением вашей проблемы является увеличение extra_float_digits
перед выдачей SELECT
-query:
pg_query($connection, "set extra_float_digits = 3");
Кроме того, вы также можете указать это изменение при подключении к вашей базе данных, добавив options
на ваш строка подключения следующее:
$connection = pg_connect("host=localhost port=5432 dbname=test user=php password=pass connect_timeout=5 options='-c extra_float_digits=3'");
Другим вариантом будет установить этот флаг в postgresql.conf
файл конфигурации сервера PostgreSQL, если у вас есть доступ к серверу и вы хотите изменить эту опцию глобально.
Другое решение — PostgreSQL возвращает другую строку в бэкэнд PHP. Это может быть достигнуто путем приведения ваших столбцов к типам с различным форматированием по умолчанию, что позволяет избежать обрезания некоторых цифр. В вашем случае вы могли бы либо бросить на integer
или же double precision
вместо использования
select cultivated_land from table
вы могли бы использовать
select cultivated_land::integer from table
или же
select cultivated_land::double precision from table
Глядя на указанные вами данные, я заметил, что все числовые значения, кроме тех столбцов, в которых указаны проценты, содержат целые числа, следовательно, использование integer
Тип данных больше подходит в этом случае. Он может соответствовать всем целочисленным значениям этой таблицы (максимально 149 000 000, поэтому bigint
не требуется), требует тот же размер хранилища, что и real
(4 байта) и подразумевает форматирование целых чисел по умолчанию, которое вы ищете.
Как упоминалось выше, интерфейс PostgreSQL-PHP работает так, что все значения, отправляемые из PostgreSQL в PHP, форматируются в виде строки в зависимости от типа. Ни один из pg_fetch_*
ни функции pg_copy_to
обеспечит необработанные значения и все эти функции конвертируют значения в строки одинаково. Насколько мне известно, текущий интерфейс PHP не даст вам ничего, кроме строки (что, на мой взгляд, не лучший дизайн интерфейса).
Причина 18.22
возвращается как 18.2199993
можно найти в том, как PostgreSQL конвертирует float4
в строки. Вы можете проверьте код о том, как PostgreSQL внутренне использует float4out
и найдите соответствующую строку, которая выполняет преобразование строки:
snprintf(ascii, MAXFLOATWIDTH + 1, "%.*g", ndig, num);
num
это float4
-номер для печати в виде строки. Обратите внимание, однако, что C будет продвигать float
-вариант к double
-переменную при звонке snprintf
, Это преобразование в двойную точность приводит к значению 18.219999313354492
вот почему вы в конечном итоге видите 18.2199993
(Вы можете проверить это Вот а также найдете некоторые подробности о представлении чисел с плавающей точкой на этом сайте).
Вывод сообщение, что все ваши float4
значения будут преобразованы с помощью этой функции, и единственный параметр, на который вы можете повлиять, это ndig
варьируя extra_float_digits
Однако ни одно значение для этой переменной не удовлетворит все ваши потребности в представлении значений так, как вы хотите. Так что, пока вы продолжаете использовать float4
в качестве типа данных и использовать текущий PHP-интерфейс для получения данных вы столкнетесь с этими проблемами.
Поэтому я все же рекомендую выбирать разные типы данных для ваших столбцов. Если вы думаете, что у вас есть требование для десятичных чисел, вы можете исследовать decimal
типы данных где вы можете указать точность и масштаб в соответствии с вашими требованиями. Если вы хотите придерживаться чисел с плавающей запятой, я предлагаю округлить значения в PHP, прежде чем отображать их пользователю.
Других решений пока нет …