You are not logged in. Login Now
 0-10          
 
Author Message
nharmon
When is 3 not really 3? Mark Unseen   Dec 27 20:18 UTC 2006

Background: I want to store a collection of weekdays into a database
field as a single number. So, following chmod's example I have assigned
each day of the week a power of 2 and then add them together to get the
composite.

IE:   Monday, Wednesday, Friday = 2^0 + 2^2 + 2^3 = 21

So, now how do you go backwards? Here is what I hammered out:

while ( $number > 0 ) {
        $theday = 1 + floor(log($number,2));
        $day[$theday] = "X";
        $number = $number - pow(2,floor(log($number,2)));
}

So after this, $day should be an array containing an "X" for each day of
the week. And it seems to work just fine, except in some cases. I have
traced the problem to the following:

floor(log(64,2)) = 5???

So, why is 5 the floor of 6? This also happens with 3 (floor of 3
becomes 2 for some reason), but not to 1, 2, 4, or 5. Am I missing
something?
10 responses total.
easlern
response 1 of 10: Mark Unseen   Dec 27 20:36 UTC 2006

I'm not sure about the math stuff, but I think the usual way to find the
values is to use something like:
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 4
THURSDAY = 8
...

if (day | MONDAY) print "there's a monday in this value thing.";

Is that how you intended to use it?
nharmon
response 2 of 10: Mark Unseen   Dec 27 20:51 UTC 2006

That is exactly how I intended to use it. But for some reason the floor
of log(64,2) is coming out as 5.
nharmon
response 3 of 10: Mark Unseen   Dec 27 20:55 UTC 2006

Ok, so I changed the while loop to this:

while ( $number > 0 ) {
        echo "Number: $number <br>\n";
        $theday = 1 + floor(log($number,2)+0.0002);
        echo "Day: $theday <br>\n";
        echo "<br>\n";
        $day[$theday] = "X";
        $number = $number - pow(2,floor(log($number,2)+0.0002));
}

And now everything works ok. Weird!
cross
response 4 of 10: Mark Unseen   Dec 27 21:59 UTC 2006

ew ew ew.

Regarding #1; Surely you meant `&'.

Regarding #0, #2, #3; Don't use exponential functions.  Ew ew ew.  Use shift
and bit test operators: then you won't end up with wierd things as you
convert to floating point representation and back to integers.  Here's my
routine:

use strict;
use warnings;

my ( $m, $t, $w, $r, $f, $s, $u );
$m = 1 << 0;
$t = 1 << 1;
$w = 1 << 2;
$r = 1 << 3;
$f = 1 << 4;
$s = 1 << 5;
$u = 1 << 6;

my $d1 = $m | $w | $f;
my $d2 = $s | $u;

# Here is the magic.
sub printdays
{
        my $d = shift;
        my @days = ('Nul', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun');

        $d &= ($m | $t | $w | $r | $f | $s | $u);
        foreach my $bit (1..7) {
                my $day = 0;
                if ($d & (1 << $bit - 1)) {
                        $day = $bit;
                }

                print $days[$day], "\n" if ($day)
        }
}

printdays $d1;
printdays $d2;

For things like this, always prefer integer arithmetic over floating point.
easlern
response 5 of 10: Mark Unseen   Dec 28 14:18 UTC 2006

Oops- yeah, use bitwise and to test.
nharmon
response 6 of 10: Mark Unseen   Dec 28 14:41 UTC 2006

Wow, thanks Dan. I see how treating the days as binary place values
makes things a lot easier. I do have one question though. What does  &=
  do? It isn't in my list of operators. :)
pfv
response 7 of 10: Mark Unseen   Dec 28 16:55 UTC 2006

a&=b; //and 'a' & 'b' - stored back to 'a'

gull
response 8 of 10: Mark Unseen   Dec 28 20:01 UTC 2006

I'm pretty sure the problem you were seeing was due to round-off error.  
Because of the conversion to floating point, you probably weren't 
actually getting 6, but rather 5.99999...

As cross points out, using bitwise operations eliminates that problem.  
It's also faster.
nharmon
response 9 of 10: Mark Unseen   Dec 28 21:22 UTC 2006

One good thing about working on computers is that there is always
something to learn and be amazed about.
cross
response 10 of 10: Mark Unseen   Dec 28 21:45 UTC 2006

Unfortunately, computers force you to face the hegemony of the real number
field as the basis of elementary mathematics for most people; your method
makes a lot of sense, from a mathematical perspective, but breaks down on a
computer because computers can't really represent real numbers, but rather
rational approximations.  This is usually okay, since the rationals are
dense in the reals and with sufficient percision, the error is neglibible
for most applications.

But every now and then, you run into a problem (like this one) where the
error between the rational approximation and the real value cannot be
ignored, and then your mathematical intuition fails you and you're left
realizing that you need a representational intuition as well; specifically,
you need to understand the p-adic (where, in this case, p = 2)
representation of integers (and their ratios).

It's a bummer!
 0-10          
Response Not Possible: You are Not Logged In
 

- Backtalk version 1.3.30 - Copyright 1996-2006, Jan Wolter and Steve Weiss