http://qs321.pair.com?node_id=689327


in reply to Re: Calculating base difference of numbers
in thread Calculating base difference of numbers

printf in the instance you have shown doesn't give me anything I can work with
I should have mentioned that the rates are comming in live 24/7 and I want to look at the base point movement as the rates change.
In the examples above I'm looking to see the base point movements of +1,+4 and -5.
Whatever solution I have to get this number must be simple enough that I can use in a function where any number with any number of decimal places is looked at and the difference to last known rate instance is calculated.

Replies are listed 'Best First'.
Re^3: Calculating base difference of numbers
by almut (Canon) on May 30, 2008 at 20:20 UTC
    In the examples above I'm looking to see the base point movements of +1,+4 and -5.

    I'm not really sure I understand, but maybe something like this?

    #!/usr/bin/perl use strict; use warnings; print base_point_movement($_),"\n" for ( 1.5553 - 1.5552, 0.9984 - 0.998, 100.25 - 100.3, ); sub base_point_movement { my $diff = shift; (sprintf "%+e", $diff) =~ /([+-]\d)\./; return $1; }

    Output:

    +1 +4 -5

    Update: as has been pointed out, this only works for +/-9 — which is not too surprising, as it just extracts the before-comma digit of the difference in exponential floating-point representation  (not knowing what a "base point" is defined as, I figured it might suffice...).

    Anyhow, here's another variant, kind of extending the idea to what might have been meant — though honestly, I don't have the foggiest whether that's what the OP had in mind.  (Of course, this will also run into problems when the number of significant digits exceeds floating-point precision.)

    sub base_point_movement { my $bpm = sprintf "%+e", shift; $bpm =~ s/0*e.*$//; $bpm =~ tr/.//d; return $bpm; } print base_point_movement($_),"\n" for ( 1.5553 - 1.5552, # +1 1.5553 - 1.55, # +53 1.56 - 1.55, # +1 155.53 - 155.52, # +1 15553 - 15552, # +1 1555300 - 1555200, # +1 1555300 - 155520, # +139978 1555.300 - 1555.20, # +1 1555300 - 1555201, # +99 1.5553 - 1.555201, # +99 1.5553 - 1.55521, # +9 1.5553 - 1.555301, # -1 1.5553 - 1.55530101, # -101 1.5553 - 1.5553101, # -101 15553 - 15552.9999, # +1 15553.001 - 15552.999, # +2 15553.001 - 15552.9999, # +11 15553.1 - 15552.99, # +11 # etc. (you get the idea :) );

      Sadly this will fail for any movement of greater than 9 points. This problem is insoluble without a definition of what constitutes a point. Consider the upper number pair and some possible variations that could exist with trailing zero truncation:

      1.5553 - 1.5552 # 1 point 1.5553 - 1.55 # 53 points (your sub will fail with this) 1.56 - 1.55 # 1 point? 100 points? (insoluble from data alone)

      You either have to decide what is a point, or guess and accept that there will be errors:

      sub points { my ($was,$is,$exp) = @_; unless ($exp) { $was =~ m/\.(\d+)$/; my $x = $1 ? length($1) : 0; $is =~ m/\.(\d+)$/; my $y = $1 ? length($1) : 0; $exp = $x > $y ? $x : $y; } print "($exp) $was => $is\t"; $exp = 10**$exp; my $dif = $is*$exp - $was*$exp; # add correction factor to allow int to round correctly # also ensures that FP "error" such as 4.999999 ends up as +5 $dif += $dif < 0 ? -0.5 : +0.5; return sprintf "%+d", $dif; } print points(1.5553,1.5552), $/; print points(1.55,1.5553 ), $/; print points(1.55,1.56), $/; print points(1.55,1.56,4), $/; print points(0.9984,0.998), $/; __DATA__ (4) 1.5553 => 1.5552 -1 (4) 1.55 => 1.5553 +53 (2) 1.55 => 1.56 +1 (4) 1.55 => 1.56 +100 (4) 0.9984 => 0.998 -4
      This fails when the difference is 10 or more base points. Try it with test data of 1.5563 - 1.5552

      Unless I state otherwise, all my code runs with strict and warnings
      That's it almut!

      Thanks....I looked at sprintf but couldn't figure it out....still much Perl goodness to learn from these monks!

Re^3: Calculating base difference of numbers
by friedo (Prior) on May 30, 2008 at 20:05 UTC

    I don't understand why you can't simply use subtraction, regardless of the decimal truncation in either of the operands.

    You're going to end up with some floating-point drift anyway; perhaps it would be a good idea to drop the decimal points and treat everything as integers, then divide by 10n when it's time to display the number to the user. (This is the recommended way of doing currency calculations so floating point error doesn't mess with your cents.)