Home: Perl Programming Help: Beginner:
Do Until condition issue



amyth
New User

Jun 16, 2016, 7:17 PM


Views: 8801
Do Until condition issue

I'm new to Perl and have written a small utility. The purpose of the code is to repeatedly execute the gettimeofday function until I get a second offset by 0.5. So I would like 10.5 not 10.3 or 10.7. For some reason this code simply runs forever.


Code
use Time::HiRes qw(gettimeofday); 

my $t = sprintf('%d.%06d',gettimeofday());

print $t+0, "\n";
my $tint=1000;


do{
my $t = sprintf('%d.%06d',gettimeofday());
my $tint = int(($t-int($t))*10)+0;
print ($tint, "\n");
} until ($tint == 5 );

print $t, "\n";



Laurent_R
Veteran / Moderator

Jun 17, 2016, 12:31 AM


Views: 8797
Re: [amyth] Do Until condition issue

$tint is probably never equal to 5. What does the

Code
print ($tint, "\n");

code line print?
Try it with this change:

Code
} until ($tint >= 5 );

(even if that's not what you want in the end, at least it should help you debugging the code).


BillKSmith
Veteran

Jun 17, 2016, 6:53 AM


Views: 8791
Re: [amyth] Do Until condition issue

Your problem is with the accuracy of floating point arithmetic (Ref: perldoc -q "long decimal"). Laurent's solution addresses this problem, but fails if the very first value is much greater than 5. You can avoid the problem completely by working with the integer number of microseconds.


Code
use strict; 
use warnings;
use Time::HiRes qw(gettimeofday);

my ($sec, $microsec);
do{
($sec, $microsec) = gettimeofday;
} until $microsec == 500000;
printf "%d.%06d\n", $sec, $microsec;


Depending on your operating system, it may be necessary to use:

Code
} until $microsec >= 500000  and $microsec  < (500000 + $delta);

The value of $delta depends on your requirements. (I think you want to use 100000.
Good Luck,
Bill


amyth
New User

Jun 17, 2016, 3:32 PM


Views: 8782
Re: [Laurent_R] Do Until condition issue

The print command prints just the number. no decimals.

I tried >=5. The code now exits even if $tint prints as 4. or 2.

Somehow I think the issue is with the way the condition is being evaluated.


Quote
ts4200:~# ./test.pl
1466202575.66781
6
1466202575.667813
ts4200:~# ./test.pl
1466202578.70864
7
1466202578.708642
ts4200:~# ./test.pl
1466202580.74874
7
1466202580.748745
ts4200:~# ./test.pl
1466202582.41626
4
1466202582.416257
ts4200:~# ./test.pl
1466202584.25627
2
1466202584.256273



amyth
New User

Jun 17, 2016, 3:38 PM


Views: 8782
Re: [BillKSmith] Do Until condition issue

This code works. That means that there is some sort of data type issue when I'm running the condition. Any ideas? I'm stumped!


BillKSmith
Veteran

Jun 17, 2016, 9:13 PM


Views: 8777
Re: [amyth] Do Until condition issue

Your problem is the precision of floating point numbers. The FAQ that I referred you to may not seem applicable, but read it carefully. It refers you to the definitive article on the subject. (Although it does not require advanced math skills, It is definitely not easy reading)

Perl does not have data types (at least not in the sense of most other languages). All arithmetic is done in floating point. The exact floating point format depends primarily on your hardware.

In general, decimal numbers cannot be represented exactly in floating point. Because of this, it is almost never right to test for equality. (Ok, I did it in my code, but I gave you an alternative if it did not work on your system.) The reason my code does work is that "small" integers values can be represented exactly. The number of microseconds is always an integer less than 10^6.
Good Luck,
Bill


Chris Charley
User

Jun 18, 2016, 9:39 AM


Views: 8762
Re: [amyth] Do Until condition issue

One problem I see is you create a new variable $tint within the scope of the loop but the until condition tests against the $tint variable (defined as 1000) created before the loop. The loop condition cannot see the the $tint variable created within the scope of the loop.

Change

my $tint = int(($t-int($t))*10)+0;

to

$tint = int(($t-int($t))*10)+0;

Also, change

my $t = sprintf('%d.%06d',gettimeofday());

to

$t = sprintf('%d.%06d',gettimeofday());

(within the loop)

When I made that change, the loop exited when it equaled 5.


(This post was edited by Chris Charley on Jun 18, 2016, 10:16 AM)


Laurent_R
Veteran / Moderator

Jun 19, 2016, 1:46 AM


Views: 8746
Re: [Chris Charley] Do Until condition issue

Very well spotted, Chris. I'm feeling embarrassed that I did not see such a basic mistake.


Chris Charley
User

Jun 19, 2016, 7:19 AM


Views: 8742
Re: [Laurent_R] Do Until condition issue

I did not see this at first Laurent. Yes, it was a simple error.


BillKSmith
Veteran

Jun 19, 2016, 4:16 PM


Views: 8728
Re: [Chris Charley] Do Until condition issue

Chris, I too am embarrassed by this one. However, the floating point issue has validity. There are marginally enough bits in double precision, but testing for an exact match is risky. Your solution exits at the proper phase, but it could take several seconds to find the match.
Good Luck,
Bill


amyth
New User

Jun 20, 2016, 4:42 PM


Views: 8721
Re: [Chris Charley] Do Until condition issue

Thanks Chris. That explains it very clearly!