Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Re: number comparison with a twist

by Marshall (Canon)
on Mar 02, 2020 at 23:39 UTC ( [id://11113671]=note: print w/replies, xml ) Need Help??


in reply to number comparison with a twist

The input number is string, not a binary float. I would convert that string to "cents" using string operations and use the resulting integer for comparison with the DB. If this input string can describe fractions of a cent, then there is more thinking involved about how to round to integers (round up, or perhaps "round to even"). And what that would mean in the overall result.
#!/usr/bin/perl use strict; use warnings; foreach my $y (qw(19.990 19.9 19. 19.559 19.00 19.34776454540000)) { my $x =$y."00"; # at least 2 digits past the decimal $x =~ s/(\d+)(\.)(\d{2})(\d+)?/$1$3/; print "result: $y => $x\n"; } __END__ result: 19.990 => 1999 result: 19.9 => 1990 result: 19. => 1900 result: 19.559 => 1955 result: 19.00 => 1900 result: 19.34776454540000 => 1934

Replies are listed 'Best First'.
Re^2: number comparison with a twist
by anotherguest (Novice) on Mar 03, 2020 at 09:06 UTC

    I've added one more step to also catch the API returning an integer. This is now my implementation:

    $from_api .= '.' unless $from_api =~ /\./; $from_api .= '00'; $from_api =~ s,^(\d+)\.(\d{2}).*$,${1}${2},; $from_api += 0;

    This catches all corner cases I can think of.

      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";

        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.

      BTW, aside from logic issues, I don't know how you came across the idea of using "," a comma as the separator?
      Yes, this is "legal and allowed", but the difference between a . and a , can be hard to see.

      I would use the default of "/" unless there is a reason not to.
      My second choice would be vertical bar.
      My 3rd choice would be curly braces - almost never.
      I have not ever been tempted to use "," for the separator.

      $from_api =~ s,^(\d+)\.(\d{2}).*$,${1}${2},; $from_api =~ s/^(\d+)\.(\d{2}).*$/$1$2/; $from_api =~ s|^(\d+)\.(\d{2}).*$|$1$2|; $from_api =~ s{^(\d+)\.(\d{2}).*$}{$1$2};
        That was just copied from parv's earlier post. While I tend to use / as a separator when writing my own regex I usually don't care too much when copying and just leave it what it was.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11113671]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (4)
As of 2024-04-25 16:13 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found