Skip Menu |

This queue is for tickets about the Geo-Google-PolylineEncoder CPAN distribution.

Report information
The Basics
Id: 49327
Status: resolved
Priority: 0/
Queue: Geo-Google-PolylineEncoder

People
Owner: Nobody in particular
Requestors: SREZIC [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: 0.04
Fixed in: (no value)



Subject: Result of encode_signed_number wrong for small negative numbers
The attached script demonstrates that the encode_signed_number method returns wrong results for small negative numbers (approx. 0 < $number < -1e-6). The problem is that the signedness has to be determined *after* the rounding was done. In the attached script the encode_signed_number method is redefined on the fly and the 2nd call returns the correct result. If you prefer it, I can turn this into a formal patch. Regards, Slaven
Subject: polylineencoder_encode_signed_number.pl
#!/usr/bin/perl use Geo::Google::PolylineEncoder; $test_number = -0.000001; warn Geo::Google::PolylineEncoder->encode_signed_number($test_number); *Geo::Google::PolylineEncoder::encode_signed_number = sub { my ($self, $orig_num) = @_; # Take the decimal value and multiply it by 1e5, flooring the result: # Note 1: we limit the number to 5 decimal places with sprintf to avoid # perl's rounding errors (they can throw the line off by a big margin sometimes) # From Geo::Google: use the correct floating point precision or else # 34.06694 - 34.06698 will give you -3.999999999999999057E-5 which doesn't # encode properly. -4E-5 encodes properly. # Note 2: we use sprintf(%8.0f ...) rather than int() for similar reasons # (see perldoc -f int), though there's not much in it and the sprintf approach # ends up doing more of a round() than a floor() in some cases: # floor = -30 num=-30 *int=-29 1e5=-30 %3.5f=-0.00030 orig=-0.000300000000009959 # floor = 119 *num=120 int=119 1e5=120 %3.5f=0.00120 orig=0.0011999999999972 # We don't use floor() to avoid a dependency on POSIX # do this in a series of steps so we can see what's going on in the debugger: my $num3_5 = sprintf('%3.5f', $orig_num)+0; my $num_1e5 = $num3_5 * 1e5; my $num = sprintf('%8.0f', $num_1e5)+0; my $is_negative = $num < 0; # my $int = int($num_1e5); # my $floor = floor($num_1e5); # warn "floor = $floor\tnum=$num\tint=$int\t1e5=$num_1e5\t%3.5f=$num3_5\torig=$orig_num\n" # if ($floor != $num or $num != $int); # Convert the decimal value to binary. # Note that a negative value must be inverted and provide padded values toward the byte boundary # (perl ints are already manipulatable in binary, so do nothing) # Shift the binary value: $num = $num << 1; # If the original decimal value was negative, invert this encoding: if ($is_negative) { $num = ~$num; } return $self->encode_number($num); }; warn Geo::Google::PolylineEncoder->encode_signed_number($test_number);
Hi Slaven, On Tue Sep 01 08:29:46 2009, SREZIC wrote: Show quoted text
> The attached script demonstrates that the encode_signed_number method > returns wrong results for small negative numbers (approx. 0 < $number < > -1e-6). The problem is that the signedness has to be determined *after* > the rounding was done. In the attached script the encode_signed_number > method is redefined on the fly and the 2nd call returns the correct result.
Ah, oops. Show quoted text
> If you prefer it, I can turn this into a formal patch.
Either that, or I can move the codebase from my private svn repo to github and give you a commit bit... Cheers, -Steve
fixed in 0.05