使用PHP sprintf打印时,PostgreSQL实数类型将丢失整数精度

使用PHP sprintf打印时,PostgreSQL实数类型将丢失整数精度,php,postgresql,precision,Php,Postgresql,Precision,我已复制到PostgreSQL数据库中。柱状耕地km2成为柱状实物。然后我使用PHP命令 echo rtrim(rtrim(sprintf('%.10F',$v),'0'),'.'); 要在表中显示数字$v,可以使用整数和浮点,但某些值会失去精度。例如,来自美国的值1669302变为1669300,这很奇怪,因为。我原以为在保存到实际列时会丢失精度,但将列转换为双精度会使差异02再次出现,所以它就在某个地方 我认为我不需要双重精度,那么我如何才能正确显示实际值呢?请记住,有些列有小数位,而有些

我已复制到PostgreSQL数据库中。柱状耕地km2成为柱状实物。然后我使用PHP命令

echo rtrim(rtrim(sprintf('%.10F',$v),'0'),'.');
要在表中显示数字$v,可以使用整数和浮点,但某些值会失去精度。例如,来自美国的值1669302变为1669300,这很奇怪,因为。我原以为在保存到实际列时会丢失精度,但将列转换为双精度会使差异02再次出现,所以它就在某个地方


我认为我不需要双重精度,那么我如何才能正确显示实际值呢?请记住,有些列有小数位,而有些列是bigint,它们也应该正确显示。

问题似乎源于PHP返回结果的方式。这些值不会作为相应的数据类型返回,而是返回。这种格式对于实数和双精度类型是不同的,因此在转换表的列类型时会看到不同的结果。你看到这个特定结果的原因是

设置额外的浮点数 手册

注意:额外浮点数设置控制浮点值转换为文本输出时包含的额外有效位数。默认值为0时,PostgreSQL支持的每个平台上的输出都相同。增加它将产生更准确地表示存储值的输出,但可能不可移植

因此,问题的一个简单解决方案是在发出SELECT查询之前增加额外的\u浮点\u位数:

或者,您也可以在连接到数据库时指定此更改,方法是向数据库添加选项,如下所示:

$connection = pg_connect("host=localhost port=5432 dbname=test user=php password=pass connect_timeout=5 options='-c extra_float_digits=3'");
另一个选项是,如果您有权访问postgresql服务器并希望全局更改该选项,请在postgresql服务器的postgresql.conf配置文件中添加

铸造价值观 另一种解决方案是让PostgreSQL向PHP后端返回不同的字符串。这可以通过将列强制转换为具有不同默认格式的类型来实现,从而避免截断某些数字。在您的情况下,您可以强制转换为整数或双精度,即不使用

select cultivated_land from table
你可以用

select cultivated_land::integer from table

更改数据类型 查看您指定的数据,我注意到除了指定百分比的列之外,所有数值都包含整数,因此在本例中使用整数数据类型更合适。它可以适应此表的所有整数值,最大值为14900000,因此不需要bigint,它需要与real 4字节相同的存储大小,并表示您要查找的整数的默认格式

更新:PostgreSQL PHP接口和浮点表示的背景 如上所述,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数字。但是请注意,当调用snprintf时,C将调用它。此转换为双精度的结果是值18.219999313354492,这就是您最终看到18.2199993的原因。您可以检查这一点,也可以在此网站上找到有关浮点数表示的一些详细信息

附带的信息是,所有float4值都将使用此函数进行转换,您可以影响的唯一参数是通过改变额外的\u float\u数字来实现ndig,但是,此变量的任何单个值都不能满足您表示所需值的所有需要。因此,只要您继续使用float4作为数据类型,并使用当前的PHP接口获取数据,您就会遇到这些问题


因此,我仍然建议为列选择不同的数据类型。如果您认为您需要十进制数,您可能需要研究在哪里可以根据应用程序的需要指定精度和比例。如果您想坚持使用浮点数,我建议在向用户显示之前先在PHP中对值进行四舍五入。

这个问题似乎源于PHP返回结果的方式。这些值不会作为相应的数据类型返回,而是返回。这种格式对于实数和双精度类型是不同的,因此您可以在中看到 g转换表的列类型时会产生不同的结果。你看到这个特定结果的原因是

设置额外的浮点数 手册

注意:额外浮点数设置控制浮点值转换为文本输出时包含的额外有效位数。默认值为0时,PostgreSQL支持的每个平台上的输出都相同。增加它将产生更准确地表示存储值的输出,但可能不可移植

因此,问题的一个简单解决方案是在发出SELECT查询之前增加额外的\u浮点\u位数:

或者,您也可以在连接到数据库时指定此更改,方法是向数据库添加选项,如下所示:

$connection = pg_connect("host=localhost port=5432 dbname=test user=php password=pass connect_timeout=5 options='-c extra_float_digits=3'");
另一个选项是,如果您有权访问postgresql服务器并希望全局更改该选项,请在postgresql服务器的postgresql.conf配置文件中添加

铸造价值观 另一种解决方案是让PostgreSQL向PHP后端返回不同的字符串。这可以通过将列强制转换为具有不同默认格式的类型来实现,从而避免截断某些数字。在您的情况下,您可以强制转换为整数或双精度,即不使用

select cultivated_land from table
你可以用

select cultivated_land::integer from table

更改数据类型 查看您指定的数据,我注意到除了指定百分比的列之外,所有数值都包含整数,因此在本例中使用整数数据类型更合适。它可以适应此表的所有整数值,最大值为14900000,因此不需要bigint,它需要与real 4字节相同的存储大小,并表示您要查找的整数的默认格式

更新:PostgreSQL PHP接口和浮点表示的背景 如上所述,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数字。但是请注意,当调用snprintf时,C将调用它。此转换为双精度的结果是值18.219999313354492,这就是您最终看到18.2199993的原因。您可以检查这一点,也可以在此网站上找到有关浮点数表示的一些详细信息

附带的信息是,所有float4值都将使用此函数进行转换,您可以影响的唯一参数是通过改变额外的\u float\u数字来实现ndig,但是,此变量的任何单个值都不能满足您表示所需值的所有需要。因此,只要您继续使用float4作为数据类型,并使用当前的PHP接口获取数据,您就会遇到这些问题


因此,我仍然建议为列选择不同的数据类型。如果您认为您需要十进制数,您可能需要研究在哪里可以根据应用程序的需要指定精度和比例。如果您想坚持使用浮点数,我建议在向用户显示之前先用PHP将这些值四舍五入。

在PHP中节省的耕地价值是多少database@Rafiq如果没有格式化,则显示为1.6693e+06。但是我知道丢失的021669302-1669300仍然存在,因为如果我运行ALTERTABLE LandUseWP alter column type double precision,它会再次出现;年节约的耕地价值是多少database@Rafiq如果没有格式化,则显示为1.6693e+06。但是我知道丢失的021669302-1669300仍然存在,因为如果我运行ALTERTABLE LandUseWP alter column type double precision,它会再次出现;谢谢你的努力。设置额外的浮点数会将18.22这样的字段变成18.2199993,就像在pgAdmin中看到的那样,我不喜欢这样。转换这些值需要动态重写所有的动态查询,现在它们只需从$tab中选择*。更改数据类型仍然是一个选项,也许我会将所有float4转换为float8。现在有些列只包含整数,但将来我可能会发现托克劳有0.1平方公里的耕地。我不想因为int和bigint之间的区别而麻烦用户。无论如何,可能还有其他更直接的解决办法。@Rodrigo我更新了答案,提供了一些关于为什么会发生这种情况的细节。太好了!我认为现在最好的解决办法是使用十进制/
数字类型。由于任何表的行数都不会超过300行,因此占用的空间不是什么大问题。非常感谢你!谢谢你的努力。设置额外的浮点数会将18.22这样的字段变成18.2199993,就像在pgAdmin中看到的那样,我不喜欢这样。转换这些值需要动态重写所有的动态查询,现在它们只需从$tab中选择*。更改数据类型仍然是一个选项,也许我会将所有float4转换为float8。现在有些列只包含整数,但将来我可能会发现托克劳有0.1平方公里的耕地。我不想因为int和bigint之间的区别而麻烦用户。无论如何,可能还有其他更直接的解决办法。@Rodrigo我更新了答案,提供了一些关于为什么会发生这种情况的细节。太好了!我认为现在最好的解决方案是使用十进制/数字类型。由于任何表的行数都不会超过300行,因此占用的空间不是什么大问题。非常感谢你!