good chemistry is complicated,and a little bit messy -LW PerlMonks

### sub as mathematical function

 on Sep 04, 2019 at 11:23 UTC Need Help??

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

Hi, can this sub be expressed in a simple mathematical function like int(8*\$v) that returns exactly the same?
```sub quant { # input: float 0 to 1 inclusive
my \$v = shift;
if (\$v<1/9) { return 0 }
elsif (\$v<2/9) { return 1 }
elsif (\$v<3/9) { return 2 }
elsif (\$v<4/9) { return 3 }
elsif (\$v<5/9) { return 4 }
elsif (\$v<6/9) { return 5 }
elsif (\$v<7/9) { return 6 }
elsif (\$v<8/9) { return 7 }
else { return 8 }
}

Replies are listed 'Best First'.
Re: sub as mathematical function
by LanX (Archbishop) on Sep 04, 2019 at 11:35 UTC
Provided

> input: float 0 to 1 inclusive

It's  int(9*\$v)

##### Edit

Corrected off by one

##### Updated

Corrected correction ;)

##### Updated

The edge case 1=9/9 needs special treatment. Are you sure 8 is the maximum?

If yes max(8,int(9*\$v))

max needs to be imported. (POSIX?)

Cheers Rolf
(addicted to the Perl Programming Language :)
Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

min(int(9*\$x),8) is perfect thanks all :-)
Oh ... off by max, again! ;-)

##### update

I wouldn't count on correct input though, and additional max seems safe

```  DB<5> print \$_,":",max(min(int(9*\$_/18),8),0),"\n" for -2..20
-2:0
-1:0
0:0
1:0
2:1
3:1
4:2
5:2
6:3
7:3
8:4
9:4
10:5
11:5
12:6
13:6
14:7
15:7
16:8
17:8
18:8
19:8
20:8

Cheers Rolf
(addicted to the Perl Programming Language :)
Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

>> min(int(9*\$x),8)

When number of intervals vary (not only 9), then this looks more general:
my \$intervals = 9; min( int( \$intervals * \$x ), \$intervals - 1 )
or:
my \$intervals = 9; int( \$intervals * \$x ) - int \$x
Re: sub as mathematical function
by holli (Monsignor) on Sep 04, 2019 at 11:38 UTC
```sub quant{
# 14 characters, but I am bad at this, I'm sure it can get shorter
int((shift)*8)
}
Update:
Hah. I made the same mistake as LanX above. It works up to 8/9, then it fails.

```sub quant{ int((shift)*8) }

for ( 1 .. 9 ) {
print "\$_ => ", quant( (\$_/9) - 0.0001 ), "\n";
}
```1 => 0
2 => 1
3 => 2
4 => 3
5 => 4
6 => 5
7 => 6
8 => 7
9 => 7  // BOOM!
What do we learn? Test all cases :-)

holli

You can lead your users to water, but alas, you cannot drown them.

It also fails for a lot of numbers between the ones you tested (e.g. 0.112 should return 1 but returns 0, 0.23 should return 2 but returns 1, etc).

The fix is simple: Replace 8 with 9 :)

Of course, that assumes the input is in range [0..1), but that seems a sure thing.

Re: sub as mathematical function
by talexb (Canon) on Sep 04, 2019 at 13:47 UTC

This is a good example of a bad 'code smell'. If you find yourself doing a copy and paste for more than two lines .. you are probably doing it wrong. Also, a test script would have confirmed that this function had implemented the ideal behaviour.

Alex / talexb / Toronto

Thanks PJ. We owe you so much. Groklaw -- RIP -- 2003 to 2013.

Re: sub as mathematical function
by Corion (Pope) on Sep 04, 2019 at 11:26 UTC
sorry I dont get what you're saying :-( I tried floor(8*\$x), round(8*\$x), ceil(8*\$x) from the POSIX module but their all different from the sub

I linked to the documentation of the modulo operator. Your subroutine basically seems to do:

```sub mod_9 {
my( \$x ) = @_;
return \$x % 9
}

Update: No, I misread the original function. You seem to input a number with decimals and want to know into which 9-tile it falls. int (\$x*9)-1, as LanX notes below, is the correct answer then.

Re: sub as mathematical function
by ikegami (Pope) on Sep 04, 2019 at 16:20 UTC

Assuming \$v is in [0..1)

```sub quant {
my \$v = shift;
return int(\$v*9);
}

Measure (and cost) of returning "exactly the same" result unspecified, a pedant would note it's pure luck and coincidence that for 9 intervals these functions indeed seem to always return the same. For a simple case of 6 intervals:

```~\$ perl -E 'say 4 if    0.8333333333333333 < 5/6'
4
~\$ perl -E 'say int 6 * 0.8333333333333333'
5

For maybe general case of 100 intervals, playing with Data::Float (and from similar experiment literal number above came from):

```~\$ perl -MData::Float=nextdown -E '\$n=100; for(1..\$n){say if \$_-1 != i
+nt \$n*nextdown \$_/\$n}'
5
10
17
20
23
34
40
46
67
68
80
81
92
93

Create A New User
Node Status?
node history
Node Type: perlquestion [id://11105573]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?