 We don't bite newbies here... much PerlMonks

### Re^3: number comparison with a twist

by pryrt (Prior)
 on Mar 03, 2020 at 14:41 UTC Need Help??

in reply to Re^2: number comparison with a twist
in thread number comparison with a twist

Truncation is still wrong.

```use warnings;
use strict;
use POSIX qw/round/;
use Test::More tests => 2;

# somebody used a generating algorithm that used 32-bit single-precisi
+on floats, entered 1.13, but thought it was double-procision so print
+ed it into your database as %.15f; so now your api returns '1.1299999
+95231628' for a number intended to be exactly 1.13
# 1.12999999523162841796875                # exact 32-bit float repres
+entation of 1.13
sub get_from_api { '1.129999995231628' }   # sprintf '%.15f', 1.129999
+99523162841796875;

my \$from_api = get_from_api();
print "string from_api = '\$from_api' straight from api\n";
\$from_api .= '.' unless \$from_api =~ /\./;
\$from_api .= '00';
\$from_api =~ s,^(\d+)\.(\d{2}).*\$,\${1}\${2},;
print "string from_api = '\$from_api' after text manipulation\n";
\$from_api += 0;

print "bad rounding = ", \$from_api, "cents\n";
is \$from_api, 113, "should be 113 cents";

#### redo, with proper rounding
\$from_api = get_from_api();
print "string from_api = '\$from_api' straight from api\n";
\$from_api .= '.' unless \$from_api =~ /\./;
\$from_api .= '00';
\$from_api =~ s,^(\d+)\.(\d{2})(\d*).*\$,\${1}\${2}.\${3},;
print "string from_api = '\$from_api' after text manipulation\n";
\$from_api = round(\$from_api);

print "good rounding = ", \$from_api, "cents\n";
is \$from_api, 113, "should be 113 cents";

Replies are listed 'Best First'.
Re^4: number comparison with a twist
by anotherguest (Novice) on Mar 05, 2020 at 13:39 UTC

No, truncation is correct in this case. It's not that the API returns exact binary float representations, it returns prices but somehow decided to use strings as a transport and return fractional cent digits which have been all zero for now but will throw an error in my script because they CANNOT match what's in my database because this in turn stores the prizes as integer cents.

So the context in this specific case calls for truncating as the rounding error seen in my original post was introduced by storing the string representation into a binary float.
I need to use the exact value as returned by the API, not rounded, if the API is off by one cent, it is considered an error.

No, truncation is correct in this case. ... as the rounding error seen in my original post was introduced by storing the string representation into a binary float

Whatever works for you.

I am just saying that not all price strings translate into a double that is bigger than the string value, and you will want to make sure that your application doesn't have any situations like the following, for the string "1.13", which has a binary representation slightly less than 1.13 for both double precision and single precision:

```C:\usr\local\share\PassThru\perl>perl -le "print int(100*'1.13')"
112

C:\usr\local\share\PassThru\perl>perl -MPOSIX=round -le "print round(1
+00*'1.13')"
113

If your application doesn't have that problem, then great.

However, I still don't see why my suggestion won't work for you. It would be nice for you to show me one example of a price-string to put in \$from_api that will not become the right integer number of cents when run through my second section of code:

```#### redo, with proper rounding
\$from_api = get_from_api();
print "string from_api = '\$from_api' straight from api\n";
\$from_api .= '.' unless \$from_api =~ /\./;
\$from_api .= '00';
\$from_api =~ s,^(\d+)\.(\d{2})(\d*).*\$,\${1}\${2}.\${3},;
print "string from_api = '\$from_api' after text manipulation\n";
\$from_api = round(\$from_api);
print "good rounding = ", \$from_api, "cents\n";

Here's a testbed, showing that for reasonable strings, our two match. It also shows a couple strange variants, where there's at least one place where your truncation and my rounding come up with different results.

```use warnings;
use strict;
use POSIX qw/round/;
use Test::More;

for my \$correct_integer_cents ( 0 .. 200 ) {
# first, let's create a testbench using the integer number of cent
+s as the basis
my \$price_string = "00" . \$correct_integer_cents;
\$price_string =~ s/(\d{2})\$/.\$1/;
my \$price_float = \$correct_integer_cents / 100;
my \$test_name = sprintf "cents=%s str='%s' float='%.15f'", \$correc
+t_integer_cents, \$price_string, \$price_float;

# Now let's compare how your converter and my converter deal with
+these
is anotherguest(\$price_string), \$correct_integer_cents, "anothergu
+est | \$test_name";
is pryrt(\$price_string), \$correct_integer_cents,        "pryrt
+    | \$test_name";
}

# so both are identical for those 201 tests.

# however, what about weird situations
for my \$price_string ( '1.13', '1.130000000', 1.13 , sprintf('%.18f',
+1.13) ) {
is anotherguest(\$price_string), 113, "anotherguest | str='\$price_s
+tring'";
is pryrt(\$price_string), 113,        "pryrt        | str='\$price_s
+tring'";
}

done_testing;

Create A New User
Node Status?
node history
Node Type: note [id://11113709]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2020-06-01 00:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
If programming languages were movie genres, Perl would be:

Results (177 votes). Check out past polls.

Notices?