【问题标题】:Exponential values manipulation in perlperl中的指数值操作
【发布时间】:2017-05-03 16:44:45
【问题描述】:

我有一个选择语句,它以指数值返回容量,例如

Capacity=5.4835615662E+003

在 Perl 代码中

我使用的是 db2 数据库,如果我在数据库中显式运行查询,它会返回

5483.5615662

但是当我在不匹配的情况下使用容量值时使用下一个选择查询时

例如伪代码如下,

my $capacity = 'SELECT capacity FROM table';

# it returns $capacity = 5.4835615662E+003

my $result = "SELECT MEASUREMENT FROM TABLE WHERE CAPACITY = $capacity";

这里$capacity5.4835615662E+003,所以它不匹配表中的任何行。应该是5483.5615662

如何在不四舍五入的情况下将指数值转换为浮点数?

【问题讨论】:

  • 不要将$capacity 插入到 SQL 中。相反,请使用placeholders
  • SELECT MEASUREMENT FROM TABLE WHERE CAPACITY=(SELECT CAPACITY FROM TABLE where ..?) 如果您想将所有十进制数字保留在db2 级别(无需获取容量)。
  • 如果您真的拥有5.4835615662+003,那么就出了问题,您需要准确说明您是如何从数据库中获取该字符串的。应该是5.4835615662E+003。这是一个错误,还是价值真的像那样?
  • 列数据类型是什么? "数字" != "浮点数"

标签: perl db2 exponential


【解决方案1】:

您正在将$capacity 的值插入到字符串中。相反,您应该使用placeholders,如下所示:

 my $sth = $dbh->prepare(q{SELECT MEASUREMENT FROM TABLE WHERE CAPACITY=?});
 $sth->execute($capacity);

很难说是否还有其他问题,因为您提供的代码 sn-ps 并没有真正做任何事情。

很可能存储在数据库中的数字不完全是5483.5615662,而这只是您查询时显示的字符串。

如果可能,我建议您采纳@Сухой27 的建议,让数据库为您完成工作:

SELECT MEASUREMENT FROM TABLE
WHERE CAPACITY = (SELECT CAPACITY FROM TABLE where ..?)

或者,提前确定小数点后多少位真正重要并使用ROUND 或类似功能:

 my $sth = $dbh->prepare(q{
     SELECT MEASUREMENT FROM TABLE
     WHERE ROUND(CAPACITY, 6)=ROUND(?, 6)
 });
 $sth->execute($capacity);

【讨论】:

  • 是否有一些保证 perl 将始终遵循 db2 的想法,即确切的十进制值是多少?
  • @Сухой27:Perl DBI 会将二进制浮点值传递给 SQL 引擎进行比较。但是,不能保证已转换为文本字符串并再次返回的值(因为 Perl 在要求打印 5483.5615662 时不会显示 5.4835615662+003)将等于原始值。浮点比较需要精确的逐位相等,打印输出中不可见的差异很容易导致相等测试失败。
  • 你的诊断是错误的。 perl -E '$x = 5483.5615662; say qq{$x};' 按预期打印 5483.5615662。只有sprintf '%e' 会产生这种格式。 必须有一些代码强制对该值使用科学记数法,将其转换为只有 11 个有效数字的字符串,该字符串绑定不相等(当转换回float) 到原始数据库值。
  • @SinanÜnür:我同意。我在挑战 “您正在将 $capacity 的值插入一个字符串”,假设您的意思是通过字符串化添加指数。我举了一个反例。但这整件事是旧的从不比较浮点值是否相等的复杂性,我不希望 Perl 值(即使是通过占位符传递)等于它起源的数据库值.
  • @SinanÜnür:comment from Сухой27 是唯一可能的解决方案,但即便如此,我也不希望来自不同表的值相同,它们只是名义上表示相同的值。这就是为什么我们在表格上有 ID 字段的原因。
【解决方案2】:

请看Why doesn't this sql query return any results comparing floating point numbers?

我担心您在问题中显示的5.4835615662+003。这不是数字的有效表示,它仅表示5.4835615662 + 3。您需要在指数之前使用 Ee 才能按原样使用它

comparing floating-point values 还存在一个问题,即两个基本相等的数字可能具有稍微不同的二进制表示,因此不会比较相等。如果您的值已转换为字符串(这似乎很有可能,因为 Perl 不会使用指数来显示 5483.5615662 除非被告知这样做)并再次转换为浮点数,那么它极不可能导致 完全相同的值。你的比较总是会失败

在 Perl 和大多数其他语言中,数值没有特定的格式。例如,如果我运行这个

perl -E 'say 5.4835615662E+003'

我得到了输出

5483.5615662

表明两个字符串表示是等价的

这将有助于准确了解您是如何从数据库中获得 $capacity 的值的,因为如果它是一个简单的数字,那么它就不会使用科学表示。您必须使用 sprintf 才能获得您所显示的内容

SQL也是一样的,不关心数字的格式,只要有效就行,所以如果你写了

SELECT measurement FROM table WHERE capacity = 5.4835615662E+003

那么您会得到一个结果,其中capacity 恰好 等于该值。但是由于它已被修剪为 11 位有效数字,因此您极不可能找到该值的来源记录,除非它包含 5483.56156620000000000



更新

如果我跑步

perl -MMath::Trig=pi -E 'for (0 .. 20) { $x = pi * 10**$_; say qq{$x}; }'

我得到了这个结果

3.14159265358979
31.4159265358979
314.159265358979
3141.59265358979
31415.9265358979
314159.265358979
3141592.65358979
31415926.5358979
314159265.358979
3141592653.58979
31415926535.8979
314159265358.979
3141592653589.79
31415926535897.9
314159265358979
3.14159265358979e+015
3.14159265358979e+016
3.14159265358979e+017
3.14159265358979e+018
3.14159265358979e+019
3.14159265358979e+020

所以默认情况下,Perl 不会使用科学记数法,直到值达到 1015。它显然不适用于5483.5615662。某些东西将问题中的浮点值强制转换为科学计数法中不太精确的字符串。比较平等没有成功的机会

【讨论】:

  • 我的错误是 5.4835615662E+003 而已。但是当我在 where 条件下使用它时,它不会像预期的那样返回任何结果 5483.5615662
  • @ShardaDeshmukh:我认为你的诊断是错误的。比较浮点数是否相等是不可取的,尤其是当其中一个值已转换为字符串并返回时。 Perl 和我知道的任何 SQL 都不会为5483.5615662 返回5.4835615662E+003,并且不知何故,您已经将该值转换为具有该格式的字符串。你没有显示真正的代码,也不可能知道这是怎么发生的。您是否阅读了我的答案中的链接?
猜你喜欢
  • 2016-04-10
  • 1970-01-01
  • 2019-01-18
  • 2011-09-14
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 2011-07-03
  • 1970-01-01
相关资源
最近更新 更多