Ruanae has asked for the wisdom of the Perl Monks concerning the following question:

Hi I'm writing some code to calculate the phase of some data. The calculation is (X-Y)/Z but the phase is only the remainder of the number... E.G. If the calculation worked out to be 2.543 then the phase is 0.543. Is there anyway of getting just the part of a number past the decimal point? I apologise if there is a simple solution to this but I only started learning perl yesterday! Thanks in advance

Replies are listed 'Best First'.
Re: Coping with decimals
by Zaxo (Archbishop) on Jun 02, 2005 at 09:34 UTC

    my $calc = ($X-$Y)/$Z; my $phase = $calc - int $calc;

    After Compline,
    Zaxo

      Be careful with this technique as storage of decimals in a computer is never accurate and a conversion to int could throw you off.

      See this node for a discussion on the same lines

      Dividing and format

      Specifically the links in my reply here:

      Ninthwave's reply in Dividing and format


      "No matter where you go, there you are." BB
      Be also careful if you have negative numbers...

      e.g. $X = 1, $Y = 7, $Z = 5

      Then:

      ($X-$Y) = -6, so $calc = ($X-$Y)/$Z = -1.2

      but $phase = $calc - int $calc = -1.2 -1 = -2.2, which is not what you wanted...

      --------------------------------
      An idea is not responsible for the people who believe in it...

        Did you try it?

        $ perl -e'my ($X,$Y,$Z)=(1,7,5);my $c=($X-$Y)/$Z;print $c-int$c, $/' -0.2 $
        That may not be what's wanted, either.

        After Compline,
        Zaxo

      Many Thanks :-)
Re: Coping with decimals
by BrowserUk (Patriarch) on Jun 02, 2005 at 09:43 UTC
    #! perl -slw use strict; use POSIX qw[ modf ]; my $float = 2.543; my( $fract, $integral ) = modf( $float ); print "$integral - $fract"; __END__ P:\test>462825 2 - 0.543

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
Re: Coping with decimals
by Corion (Patriarch) on Jun 02, 2005 at 09:30 UTC

    What have you tried already?

    While Perl is a programming language with many special features, it still has the feature set that you can expect from other programming languages. Even if you don't know how to express this in Perl syntax, you should still be able to come up with a set of instructions that does what you want.

    For example, the following set of instructions does what you want and should be easily implemented in Perl:

    1. If the absolute of the number x is smaller than 1.0, return the number
    2. If the number x is larger than 1, return the phase of the number x-1
    3. If the number x is smaller than -1, return the phase of the number x+1

    It's easy to convince yourself that this will always produce a solution.

Re: Coping with decimals
by tlm (Prior) on Jun 02, 2005 at 13:36 UTC

    Given that you're dealing with phases, presumably with period 1, if your calculation can produce negative numbers, then I would recommend:

    $x - POSIX::floor( $x );
    instead of
    $x - int( $x );
    or
    ( POSIX::modf( $x ) )[ 0 ];

    the lowliest monk

Re: Coping with decimals
by holli (Abbot) on Jun 02, 2005 at 09:37 UTC
    several ways come to my mind.
    my $phase; my $number = 2.543; #substr $phase = "0" . substr($number,index($number,".")); #regex $number =~ /(\..+)/; $phase = "0$1"; #int $phase = $number-int($number);


    holli, /regexed monk/
      If the number doesn't contain a decimal part, you could end up using $1 from a previous match. I'd suggest (maybe some parenthesis can be avoided, but I'm not able to test ATM):
      $phase = '0' . (($number =~ /(\..+)/) ? $1 : '');
      I found particularly wit to include the "." inside $1, in order to avoid repetition.

      Flavio (perl -e 'print(scalar(reverse("\nti.xittelop\@oivalf")))')

      Don't fool yourself.

      I can't agree with your solution. The questioner is calculating a phase. Doing math. Your solution is based on string manipulation. Conflating text values and numeric values leads to errors. Refrain from smearing your mental map of a problem space.

      Be Appropriate && Follow Your Curiosity

        The standard Perl approach to rounding numbers (with sprintf) amounts to using a string manipulation for what is arguably an arithmetic task. Did you have any particular error in mind?

        When I first saw the question, the two solutions I thought of were $x - int( $x ) or indeed something along the lines of $x =~ s/\d+(?=\.)//, and I'm curious about what errors could have resulted from the latter approach.

        the lowliest monk