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

My code is doing something I really can't understand. I'm performing some linear interpolation, and test the result against the number 75. I can get two different results depending on where the &adjust_hue subroutine is called. Here's the code
use strict; use constant HUE_RANGE => 255; my @HUES = ( 0, 25, 50, 75, 120, 168, 195, 240, + 268, 315); # 0.0 0.1 0.2 0.3 0.4 0.5 + 0.6 0.7 0.8 0.9 1.0 &adjust_hue(0.3); my @hues = (0 .. 4); $_ *= 0.1 for @hues; for (@hues) { &adjust_hue($_); } sub adjust_hue { my ($scale) = @_; my @hues = @HUES; push @hues, HUE_RANGE unless $hues[-1] == HUE_RANGE; # warn 'hues ', Dumper \@hues; # warn "n hues ", (scalar @hues), "\n"; my $n_unique_hues = -1 + scalar @hues; my $scaled_index = $scale*$n_unique_hues; my $index = int $scaled_index; my $adjusted_hue = $hues[$index] + ($scaled_index - $index)*($hues +[$index + 1] - $hues[$index]); # $adjusted_hue = eval $adjusted_hue; # This shouldn't be necessary +. Somehow this sub was producing strings instead of numbers. Later, t +his strings would fail comparisons. if ($adjusted_hue == 75) { warn " adjusted_hue ($adjusted_hue) equals 75\n"; } else { warn " adjusted_hue ($adjusted_hue) does not equal 75\n"; } warn "scale $scale, scaled_index $scaled_index, index $index, adju +sted_hue $adjusted_hue\n\n"; $adjusted_hue; }
The output is the following for me:
adjusted_hue (75) equals 75 scale 0.3, scaled_index 3, index 3, adjusted_hue 75 adjusted_hue (0) does not equal 75 scale 0, scaled_index 0, index 0, adjusted_hue 0 adjusted_hue (25) does not equal 75 scale 0.1, scaled_index 1, index 1, adjusted_hue 25 adjusted_hue (50) does not equal 75 scale 0.2, scaled_index 2, index 2, adjusted_hue 50 adjusted_hue (75) does not equal 75 scale 0.3, scaled_index 3, index 3, adjusted_hue 75 adjusted_hue (120) does not equal 75 scale 0.4, scaled_index 4, index 4, adjusted_hue 120
I really don't understand why the comparison to 75 is true in one case with the parameter 0.3 and false also with the parameter 0.3 Thanks, John

Replies are listed 'Best First'.
Re: Why is this comparison failing?
by dave_the_m (Monsignor) on Oct 08, 2015 at 19:17 UTC
    Try replacing the data output line with:
    warn sprintf "scale: %.40f\nscaled_index: %.40f\n" . "index: %.40f\nadjusted_hue: %.40f\n\n", $scale, $scaled_index, $index, $adjusted_hue;
    then you'll see the following two outputs for the supposedly identical 0.3 cases:
    adjusted_hue (75) equals 75 scale: 0.2999999999999999888977697537484345957637 scaled_index: 3.0000000000000000000000000000000000000000 index: 3.0000000000000000000000000000000000000000 adjusted_hue: 75.0000000000000000000000000000000000000000 adjusted_hue (75) does not equal 75 scale: 0.3000000000000000444089209850062616169453 scaled_index: 3.0000000000000004440892098500626161694527 index: 3.0000000000000000000000000000000000000000 adjusted_hue: 75.0000000000000142108547152020037174224854

    Dave.

      Dave- Thanks. I should have known better. But I was misled by what I assumed was Perl's rules for how variables were used to build double-quoted strings. I would have thought that floating point numbers that are not integers would show enough decimal places to indicate such. That's how I was misled into thinking that these cases were identical. I realize that I actually don't know how numbers are evaluated for double-quoted strings. Do you have a good reference? Regards, John
        It's not really about string representation or integers. It may be more about the limits of Perl 5 and most programming languages in dealing with fractions and floating point. For example:
        perl -Mstrict -w -e 'if (0.3 == 0.1 * 3) { print "equals\n" } else { p +rint "* difference * ", abs(0.3 - 0.1 * 3), "\n" }' * difference * 5.55111512312578e-17
        Ron
        I'm not sure it's documented anywhere. The number of significant digits it uses varies by platform, but typically it stringifies a float to 15 significant digits, with trailing 0's excised.

        Dave.

Re: Why is this comparison failing?
by toolic (Bishop) on Oct 08, 2015 at 19:29 UTC
Re: Why is this comparison failing?
by neilwatson (Priest) on Oct 08, 2015 at 18:36 UTC

    Things to try.

    1. use warnings;
    2. Don't perform operations on $_. Assign to your own var and operate on that.
    3. For my $next_hue ( @hues ) would be safer than a for loop.

    Neil Watson
    watson-wilson.ca