如何通过测量 float 类型的时间间隔(以秒为单位)来防止浮点除法变成整数除法
How to prevent that a floating point divistion turns into integer devision by measuruing time intervals in seconds of type float
我尝试在 Freebsd 12 下使用 perl 5.20 版计算批量复制过程的传输速率。我得到了奇怪的结果,因为亚秒间隔内的值始终为零,尽管使用浮点除法来获得分形秒持续时间条目。
详情:
对于测量,我使用 DateTime::Hires 并在名为 markTime
和 diffTime
的两个子例程中提取时间标记和差异。要查看每个步骤的进展情况。
#!/usr/bin/env perl
use v5.20.1;
use warnings;
use strict;
use bigint;
use Time::HiRes qw(usleep nanosleep);
use DateTime::HiRes;
use Scalar::Util::Numeric;
# Set the total size of an object
my $ttlSize = 1E3;
# Set the start time
my $startTime = &markTime('START');
# Sleep 12500 µs
usleep(12500);
# Calculate the elapsed time
my $runTime = &diffTime($startTime);
# Calculate the rate in Bytes per second
my $rate = $ttlSize/$runTime;
# Print it
print "RATE: $rate Bytes/s\n";
# Routine to set a timestamp
sub markTime () {
my $prefix = shift;
$prefix ='NOW' if not $prefix;
my $now = DateTime::HiRes->now;
print"MARK.$prefix: ",$now->strftime( '%Y-%m-%d-%H-%M-%S.%N' ),"\n";
return $now;
}
# Routine calc the difference
sub diffTime () {
my $start = shift;
# Calculate the duration
my $now = &markTime('DIFF');
my $dur = $now - $start;
my $nano = $dur->in_units('nanoseconds');
# Original dateTime duration is integer
print "DT.DUARTION: ",$nano," ns IS.INT: ",
Scalar::Util::Numeric::isint($nano),"\n";
# Calc values for mano, micro, milli and seconds
my $math_ns = sprintf("%e",$nano);
my $math_mus = sprintf("%e",$math_ns/1.0E3);
my $math_ms = sprintf("%e",$math_ns/1.0E6);
my $math_s = sprintf("%e",$math_ns/1.0E9);
# Show the calculations
print"MATH: ",$math_ns," ns\n";
print"MATH: ",$math_mus," µs\n";
print"MATH: ",$math_ms," ms\n";
print"MATH: ",$math_s," s\n";
# Check if the stuff ist float
print "TIME.SECS: ", $math_ns*1.0E-9,
" TIME.NANO: " ,$math_ns,
" IS.FLOAT: " ,Scalar::Util::Numeric::isfloat($math_ns),"\n";
return $math_s;
}
结果显示,runtime
的亚秒浮点值除法为 "cutted",传输速率变为 INF,尽管使用了浮点除法运算。
MARK.START: 2019-12-12-10-12-33.954870000
MARK.DIFF: 2019-12-12-10-12-33.968422000
DT.DUARTION: 13552000 ns IS.INT: 1
MATH: 1.355200e+07 ns
MATH: 1.355200e+04 µs
MATH: 1.300000e+01 ms
MATH: 0.000000e+00 s
TIME.SECS: 0 TIME.NANO: 1.355200e+07 IS.FLOAT: 1
RATE: inf Bytes/s
我该怎么做才能为 $runtime
变量获得正确的值 0.013552?
转储值:
---NANO---
SV = PVNV(0x8029cf9f0) at 0x8031c6408
REFCNT = 1
FLAGS = (IOK,NOK,POK,IsCOW,pIOK,pNOK,pPOK)
IV = 13574000
NV = 13574000
PV = 0x8031ab5d0 "13574000"[=12=]
CUR = 8
LEN = 10
COW_REFCNT = 1
---MATH_NS---
SV = PV(0x802cb8900) at 0x8031c64e0
REFCNT = 1
FLAGS = (POK,IsCOW,pPOK)
PV = 0x8031ab0f0 "1.357400e+07"[=12=]
CUR = 12
LEN = 16
COW_REFCNT = 2
---MATH_NS/1E9---
SV = PV(0x802d18b50) at 0x802f19bb8
REFCNT = 1
FLAGS = (TEMP,ROK)
RV = 0x8031d97c8
SV = PVHV(0x800a8a560) at 0x8031d97c8
REFCNT = 2
FLAGS = (OBJECT,SHAREKEYS)
STASH = 0x8016910d8 "Math::BigInt"
ARRAY = 0x80323cdc0 (0:7, 2:1)
hash quality = 62.5%
KEYS = 2
FILL = 1
MAX = 7
Elt "value" HASH = 0x7025df17
SV = IV(0x803162b60) at 0x803162b70
REFCNT = 1
FLAGS = (ROK)
RV = 0x801acca08
SV = PVAV(0x800a894d8) at 0x801acca08
REFCNT = 1
FLAGS = (OBJECT)
STASH = 0x801692e70 "Math::BigInt::Calc"
ARRAY = 0x802d3bb18
FILL = 0
MAX = 0
FLAGS = (REAL)
Elt No. 0
SV = IV(0x800b19ae8) at 0x800b19af8
REFCNT = 1
FLAGS = (IOK,pIOK)
IV = 0
Elt "sign" HASH = 0xc9f40697
SV = PV(0x802cb8aa0) at 0x8031c6078
REFCNT = 1
FLAGS = (POK,IsCOW,pPOK)
PV = 0x8017af0f0 "+"[=12=]
CUR = 1
LEN = 10
COW_REFCNT = 1
PV = 0x8031d97c8 ""
CUR = 0
LEN = 0
使用 Math::BigFloat
修正 diffTime
所以我算出来了,在程序中use bigint;
(现在在例子中调用)是麻烦制造者,会对所有部门产生影响。解决方法是:
sub diffTimeBigFloat () {
my $start = shift;
# Calculate the duration
my $now = &markTime('DIFF');
my $dur = $now->subtract_datetime_absolute($start);
my $nano = $dur->in_units('nanoseconds');
# Original dateTime Duration
print "DT.DUARTION: ",$nano," ns IS.INT: ",
Scalar::Util::Numeric::isint($nano),"\n";
# Calc values for mano, micro, milli and seconds
my $math_s = Math::BigFloat->new(sprintf("%sE-9",$nano));
print"MATH: ",$math_s," s\n";
print "TIME.SECS: ", $math_s,
" TIME.NANO: ", $math_s*1E9,
" IS.FLOAT: " ,Scalar::Util::Numeric::isfloat($math_s),"\n";
return $math_s;
}
use bigint;
实际上导致 1.0E9
被替换为 Math::BigInt->new(1.0E9)
。
此外,当至少一个操作数是 Math::BigInt 对象时,Math::BigInt 会覆盖除法。覆盖导致结果成为 Math::BigInt 对象。
因此,use bigint;
可以在远处产生很大的影响。因此,我个人觉得 use bigint;
太神奇了。我宁愿在适当的地方使用 Math::BigInt->new(...)
而不是让 use bigint;
将我所有的数字常量转换为 Math::BigInt 对象。
但是你走了另一条路。您不仅在使用 use bigint;
,而且在您显然不想要它的效果时也在使用它。至少,在 diffTime
内部使用 no bigint;
,这样子中的数字常量不会自动包装到 Math::BigInt 对象中。
use bigint;
# bigint isn't used by DateTime::Duration
my $nano = do { no bigint; 13552000 };
say $nano/1E9; # 0
no bigint;
say $nano/1E9; # 0.013552
但是就像我上面说的,我建议完全避免 use bigint;
以支持在适当的情况下显式创建 Math::BigInt 对象,这根本不是你的情况。
my $nano = 13552000;
say $nano/1E9; # 0.013552
如果你因为什么原因需要处理Math::BigInt对象,需要对其进行浮点运算,解决方法是将Math::BigInt对象转换成普通标量或Math::BigFloat(或Math::BigRat)对象。
可以使用 $x->numify
将 Math::BigInt 对象转换为普通标量。如果值太大,可能会溢出。
可以使用 Math::BigFloat->new($x)
.
将 Math::BigInt 对象转换为 Math::BigFloat 对象
use bigint;
use Math::BigFloat;
# bigint isn't used by DateTime::Duration
my $nano = do { no bigint; 13552000 };
say $nano/1E9; # 0
say $nano/(1E9->numify); # 0.013552
say $nano/Math::BigFloat->new(1E9); # 0.013552
不要像以前那样使用 sprintf
。这将导致不必要的精度损失。
我尝试在 Freebsd 12 下使用 perl 5.20 版计算批量复制过程的传输速率。我得到了奇怪的结果,因为亚秒间隔内的值始终为零,尽管使用浮点除法来获得分形秒持续时间条目。
详情:
对于测量,我使用 DateTime::Hires 并在名为 markTime
和 diffTime
的两个子例程中提取时间标记和差异。要查看每个步骤的进展情况。
#!/usr/bin/env perl
use v5.20.1;
use warnings;
use strict;
use bigint;
use Time::HiRes qw(usleep nanosleep);
use DateTime::HiRes;
use Scalar::Util::Numeric;
# Set the total size of an object
my $ttlSize = 1E3;
# Set the start time
my $startTime = &markTime('START');
# Sleep 12500 µs
usleep(12500);
# Calculate the elapsed time
my $runTime = &diffTime($startTime);
# Calculate the rate in Bytes per second
my $rate = $ttlSize/$runTime;
# Print it
print "RATE: $rate Bytes/s\n";
# Routine to set a timestamp
sub markTime () {
my $prefix = shift;
$prefix ='NOW' if not $prefix;
my $now = DateTime::HiRes->now;
print"MARK.$prefix: ",$now->strftime( '%Y-%m-%d-%H-%M-%S.%N' ),"\n";
return $now;
}
# Routine calc the difference
sub diffTime () {
my $start = shift;
# Calculate the duration
my $now = &markTime('DIFF');
my $dur = $now - $start;
my $nano = $dur->in_units('nanoseconds');
# Original dateTime duration is integer
print "DT.DUARTION: ",$nano," ns IS.INT: ",
Scalar::Util::Numeric::isint($nano),"\n";
# Calc values for mano, micro, milli and seconds
my $math_ns = sprintf("%e",$nano);
my $math_mus = sprintf("%e",$math_ns/1.0E3);
my $math_ms = sprintf("%e",$math_ns/1.0E6);
my $math_s = sprintf("%e",$math_ns/1.0E9);
# Show the calculations
print"MATH: ",$math_ns," ns\n";
print"MATH: ",$math_mus," µs\n";
print"MATH: ",$math_ms," ms\n";
print"MATH: ",$math_s," s\n";
# Check if the stuff ist float
print "TIME.SECS: ", $math_ns*1.0E-9,
" TIME.NANO: " ,$math_ns,
" IS.FLOAT: " ,Scalar::Util::Numeric::isfloat($math_ns),"\n";
return $math_s;
}
结果显示,runtime
的亚秒浮点值除法为 "cutted",传输速率变为 INF,尽管使用了浮点除法运算。
MARK.START: 2019-12-12-10-12-33.954870000
MARK.DIFF: 2019-12-12-10-12-33.968422000
DT.DUARTION: 13552000 ns IS.INT: 1
MATH: 1.355200e+07 ns
MATH: 1.355200e+04 µs
MATH: 1.300000e+01 ms
MATH: 0.000000e+00 s
TIME.SECS: 0 TIME.NANO: 1.355200e+07 IS.FLOAT: 1
RATE: inf Bytes/s
我该怎么做才能为 $runtime
变量获得正确的值 0.013552?
转储值:
---NANO---
SV = PVNV(0x8029cf9f0) at 0x8031c6408
REFCNT = 1
FLAGS = (IOK,NOK,POK,IsCOW,pIOK,pNOK,pPOK)
IV = 13574000
NV = 13574000
PV = 0x8031ab5d0 "13574000"[=12=]
CUR = 8
LEN = 10
COW_REFCNT = 1
---MATH_NS---
SV = PV(0x802cb8900) at 0x8031c64e0
REFCNT = 1
FLAGS = (POK,IsCOW,pPOK)
PV = 0x8031ab0f0 "1.357400e+07"[=12=]
CUR = 12
LEN = 16
COW_REFCNT = 2
---MATH_NS/1E9---
SV = PV(0x802d18b50) at 0x802f19bb8
REFCNT = 1
FLAGS = (TEMP,ROK)
RV = 0x8031d97c8
SV = PVHV(0x800a8a560) at 0x8031d97c8
REFCNT = 2
FLAGS = (OBJECT,SHAREKEYS)
STASH = 0x8016910d8 "Math::BigInt"
ARRAY = 0x80323cdc0 (0:7, 2:1)
hash quality = 62.5%
KEYS = 2
FILL = 1
MAX = 7
Elt "value" HASH = 0x7025df17
SV = IV(0x803162b60) at 0x803162b70
REFCNT = 1
FLAGS = (ROK)
RV = 0x801acca08
SV = PVAV(0x800a894d8) at 0x801acca08
REFCNT = 1
FLAGS = (OBJECT)
STASH = 0x801692e70 "Math::BigInt::Calc"
ARRAY = 0x802d3bb18
FILL = 0
MAX = 0
FLAGS = (REAL)
Elt No. 0
SV = IV(0x800b19ae8) at 0x800b19af8
REFCNT = 1
FLAGS = (IOK,pIOK)
IV = 0
Elt "sign" HASH = 0xc9f40697
SV = PV(0x802cb8aa0) at 0x8031c6078
REFCNT = 1
FLAGS = (POK,IsCOW,pPOK)
PV = 0x8017af0f0 "+"[=12=]
CUR = 1
LEN = 10
COW_REFCNT = 1
PV = 0x8031d97c8 ""
CUR = 0
LEN = 0
使用 Math::BigFloat
修正 diffTime所以我算出来了,在程序中use bigint;
(现在在例子中调用)是麻烦制造者,会对所有部门产生影响。解决方法是:
sub diffTimeBigFloat () {
my $start = shift;
# Calculate the duration
my $now = &markTime('DIFF');
my $dur = $now->subtract_datetime_absolute($start);
my $nano = $dur->in_units('nanoseconds');
# Original dateTime Duration
print "DT.DUARTION: ",$nano," ns IS.INT: ",
Scalar::Util::Numeric::isint($nano),"\n";
# Calc values for mano, micro, milli and seconds
my $math_s = Math::BigFloat->new(sprintf("%sE-9",$nano));
print"MATH: ",$math_s," s\n";
print "TIME.SECS: ", $math_s,
" TIME.NANO: ", $math_s*1E9,
" IS.FLOAT: " ,Scalar::Util::Numeric::isfloat($math_s),"\n";
return $math_s;
}
use bigint;
实际上导致 1.0E9
被替换为 Math::BigInt->new(1.0E9)
。
此外,当至少一个操作数是 Math::BigInt 对象时,Math::BigInt 会覆盖除法。覆盖导致结果成为 Math::BigInt 对象。
因此,use bigint;
可以在远处产生很大的影响。因此,我个人觉得 use bigint;
太神奇了。我宁愿在适当的地方使用 Math::BigInt->new(...)
而不是让 use bigint;
将我所有的数字常量转换为 Math::BigInt 对象。
但是你走了另一条路。您不仅在使用 use bigint;
,而且在您显然不想要它的效果时也在使用它。至少,在 diffTime
内部使用 no bigint;
,这样子中的数字常量不会自动包装到 Math::BigInt 对象中。
use bigint;
# bigint isn't used by DateTime::Duration
my $nano = do { no bigint; 13552000 };
say $nano/1E9; # 0
no bigint;
say $nano/1E9; # 0.013552
但是就像我上面说的,我建议完全避免 use bigint;
以支持在适当的情况下显式创建 Math::BigInt 对象,这根本不是你的情况。
my $nano = 13552000;
say $nano/1E9; # 0.013552
如果你因为什么原因需要处理Math::BigInt对象,需要对其进行浮点运算,解决方法是将Math::BigInt对象转换成普通标量或Math::BigFloat(或Math::BigRat)对象。
可以使用 $x->numify
将 Math::BigInt 对象转换为普通标量。如果值太大,可能会溢出。
可以使用 Math::BigFloat->new($x)
.
use bigint;
use Math::BigFloat;
# bigint isn't used by DateTime::Duration
my $nano = do { no bigint; 13552000 };
say $nano/1E9; # 0
say $nano/(1E9->numify); # 0.013552
say $nano/Math::BigFloat->new(1E9); # 0.013552
不要像以前那样使用 sprintf
。这将导致不必要的精度损失。