WeBWorK Problems

Sprint rounding convention fails

Sprint rounding convention fails

by tim Payer -
Number of replies: 3
Greetings all!

I have found a peculiar rounding problem when using the "sprint" command.
I do not think it has anything to do with absolute tolerance, but perhaps you could please shed light on this?

Let me explain:

The homework assignment below asks students to calculate Z-scores and their associated probabilities. The students are to use conventional Z-tables which require a rounding of the z-score to the second decimal and webwork is to check on this z-score value.

To round a calculation down to the second decimal I have been using the sprint command: 

$zb12 =sprintf("%0.2f",$zb1);
And then converting this variable to a math object with a "Compute()" command. All was working fine until a particular student had a z-score of 
Z = -1.125  Which should have rounded to Z = -1.13, but the sprint command clipped the third decimal and returned Z = -1.12

These Values fail to "round up" at the second decimal:

2.105  -1.105 
2.115  -1.115 
2.125  -1.125 
2.135  -1.135 
2.145  -1.145 
2.155  -1.155 

But these values will round up at the second decimal:
2.165  -1.165 
2.175  -1.175 
2.185  -1.185 
2.195  -1.195 

Is this the sprint convention?

Do you see a solution beyond the hacking "if then condition" enforcing the rounding that I put in below?

I would very much appreciate your insight as to why this rounding has not occurred.
Thanks, Tim


# DESCRIPTION   Normal probability and Continuity Correction
# Find the associated probability given mu and sigma.
# Use correct notation.
# WeBWorK problem written by TimPayer <tsp1@humboldt.edu>
# ENDDESCRIPTION

## DBsubject(Probability)
## DBchapter(Random variables)
## DBsection(Expectation)
## Institution(Humboldt State University)
## Author(Tim Payer)
## KEYWORDS(probability, translate, notation)
DOCUMENT();

loadMacros(
"PGstandard.pl",
"PGunion.pl",
"PGnumericalmacros.pl",
"PGstatisticsmacros.pl",
"MathObjects.pl",
"parserPopUp.pl",
"PGML.pl",
"unionTables.pl",
"niceTables.pl",
"PGcourse.pl",
"weightedGrader.pl"
);

install_weighted_grader();

#Text(beginproblem());   #uncomment

#install_problem_grader(~~&std_problem_grader);
$showPartialCorrectAnswers = 1;

Context("Numeric");
Context()->flags->set(
  tolerance => 0.0001,
  tolType => "absolute",
);

$tree = list_random('redwood', 'Douglass-fir', 'grand-fir', 'Western hemlock','tanoak','madrone');


$m = random(35,39,0.1);
$ls = floor($m/9) + 0.1;
$hs =ceil($m/4)- 0.3;
$s = random($ls, $hs, 0.1);

## These Do loops are just to insure decimal answers that exceed four
## decimals and thus will prompt the student round correctly before
## drawing a Z-table value.
#do { $a = random(0.1,1.9,0.1); } until (floor(10000*$a/$s) < 10000*$a/$s);
#do { $l = random(0.1,1.9,0.1); } until (floor(20000*$l/$s) < 20000*$l/$s);
$l = random(0.1,1.9,0.1);
$a = random(0.1,1.9,0.1);
$ax = floor($m-$a);
$bx = ceil($m+2*$l);
$axcc = $ax -0.5;
$bxcc = $bx +0.5;
$zxls = ($axcc - $m)/$s;
$zxus =($bxcc - $m)/$s;
$zxl2 =sprintf("%0.2f",$zxls);
$zxu2 =sprintf("%0.2f",$zxus);
$pxs = normal_prob($zxl2, $zxu2, mean=>0, deviation=>1);
$px =sprintf("%0.4f",$pxs);

## Problem 8.1a ##
$a1 = floor($m-1.2*$s);
$a1cc = $a1 -0.5;
$za = ($a1cc - $m)/$s;
$popup1 = PopUp(
["Choose:", "Y =", "Y <", "Y < or =", "Y >", "Y > or ="], "Y > or =");
$popup2 = PopUp(
["Choose:", "Y =", "Y <", "Y < or =", "Y >", "Y > or ="], "Y > or =");
$ans1 =Compute("$a1cc");
$popup3 = PopUp(
["Choose:", "Z =", "Z <", "Z < or =", "Z >", "Z > or ="], "Z > or =");
$za2 =sprintf("%0.2f",$za);
$ans2 = Compute("$za2");
$pa = normal_prob($za2, infty, mean=>0, deviation=>1);
$ans3 = Compute("$pa");


## Problem 8.1b ##
$b1cc = $a1cc;
$b2cc = $a1 +0.5;
$zb1 = ($b1cc - $m)/$s;
$zb2 = ($b2cc - $m)/$s;

$popup4 = PopUp(
["Choose:", "Y =", "Y <", "Y < or =", "Y >", "Y > or ="], "Y =");
$popup5 = PopUp(
["Choose:", $a1-1.5, $a1-1,$a1-0.5, $a1, $a1+0.5,$a1+1,$a1+1.5], $a1-0.5);
$popup6 = PopUp(
["Choose:", "= Y =", "< Y <", "< or =   Y   < or =", "=   Y   < or =", "< or =   Y   ="], "< or =   Y   < or =");
$popup7 = PopUp(
["Choose:", $a1-1.5, $a1-1,$a1-0.5, $a1, $a1+0.5,$a1+1,$a1+1.5], $a1+0.5);
$popup8 = PopUp(
["Choose:", "= Z =", "< Z <", "< or =   Z   < or =", "=   Z   < or =", "< or =   Z   ="], "< or =   Z   < or =");
$zb12 =sprintf("%0.2f",$zb1);
$zb22 =sprintf("%0.2f",$zb2);
$ans4 =Compute("$zb12");
$ans5 =Compute("$zb22");


## Enforcing rounding to the second decimal loop?###

$dif5 =abs($ans5 -$zb2);
if(($dif5 > 0.005) || ($dif5 == 0.005)){
   if($ans5 < 0){
   $ad = Compute("$ans5-.01");
   $ans5 =$ad;
   } elsif($ans5 > 0 ) {
   $ad = Compute("$ans5+.01");
   $ans5 =$ad;
   } else {
$sw = $ans5;
$ans5 = $sw;
  }
}

$tes1 = Compute("-1.125");
$test1 =sprintf("%0.2f",$tes1);
####################

$pb = normal_prob($zb12, $ans5,  mean=>0, deviation=>1);
$ans6 =Compute("$pb");

## Problem 8.1c ##
$c2= floor($m+0.8*$ka);
$c1cc = $a1cc;
$c2cc = $c2 +0.5;
$zc1 = ($c1cc - $m)/$s;
$zc2 = ($c2cc - $m)/$s;
$popup9 = PopUp(
["Choose:", "= Y =", "< Y <", "< or =   Y   < or =", "=   Y   < or =", "< or =   Y   ="], "< or =   Y   < or =");
$popup10 = PopUp(
["Choose:", $a1-1.5, $a1-1,$a1-0.5, $a1, $a1+0.5,$a1+1,$a1+1.5], $a1-0.5);
$popup11 = PopUp(
["Choose:", "= Y =", "< Y <", "< or =   Y   < or =", "=   Y   < or =", "< or =   Y   ="], "< or =   Y   < or =");
$popup12 = PopUp(
["Choose:", $c2-1.5, $c2-1,$c2-0.5, $c2, $c2+0.5,$c2+1,$c2+1.5], $c2+0.5);
$popup13 = PopUp(
["Choose:", "= Z =", "< Z <", "< or =   Z   < or =", "=   Z   < or =", "< or =   Z   ="], "< or =   Z   < or =");
$zc12 =sprintf("%0.2f",$zc1);
$zc22 =sprintf("%0.2f",$zc2);
$ans7 =Compute("$zc12");
$ans8 =Compute("$zc22");
$pc = normal_prob($zc12, $zc22,  mean=>0, deviation=>1);
$ans9 =Compute("$pc");


#######
$column1 = PGML::Format2(<<'END_PGML');

 
*Drawn from Lecture Notes: Week 5 Day 1.*  

    *8.2)*   In genetic studies of the fruit-fly _Drosphila melanogaster_, one variable of interest is the total number of bristles on the dorsal surface of the mesothorax. For a certain _Drosphila_ population, this bristle count follows a normal distribution with a mean of [`\mu`] = [``[$m]``] and a standard deviation of [`\sigma`] = [``[$s]``]. Find the following probabilities by accounting for the continuity correction.  
 
        Let [`Y`] = The total number of bristles on the dorsal surface of the mesothorax of a randomly drawn fruit fly _Drosphila_ population.  
 


*8.2a)*   Find the probability of drawing a fruit fly with [``[$a1]``] or more bristles on the dorsal surface of the mesothorax.  

[``\Large{P(}``] [$popup1->menu]* [``\Large{[$a1]) \approx}``]  
[``\Large{\approx P(}``] [$popup2->menu]* [____] [``\Large{) \, \approx }``]  
[``\Large{\approx P(}``] [$popup3->menu]* [____] [``\Large{) \, \approx }``] [____]  
 
*8.2b)*   Find the probability of drawing a fruit fly with [$a1] bristles on the dorsal surface of the mesothorax.  
 
[``\Large{P(}``] [$popup4->menu]* [``\Large{[$a1]) \approx}``]  
[``\Large{\approx P(}``] [$popup5->menu]* [$popup6->menu]* [$popup7->menu]*[``\Large{) \, \approx}``]  
[``\Large{\approx P(}``]  [____] [$popup8->menu]* [____]  [``\Large{) \, \approx }``] [____] 
 
 
*8.2c)*   Find the probability of drawing a fruit fly that has between [$a1] and [$c2] bristles (inclusive) on the dorsal surface of the mesothorax.  
 
[``\Large{P([$a1] }``] [$popup9->menu]* [``\Large{[$c2]) \approx}``]  
[``\Large{\approx P(}``] [$popup10->menu]* [$popup11->menu]* [$popup12->menu]*[``\Large{) \, \approx}``]  
[``\Large{\approx P(}``]  [____] [$popup13->menu]* [____]  [``\Large{) \, \approx }``] [____]  
 
 

END_PGML


$column2 =$BCENTER."Magnified images of the 'wild type' $BITALIC Drosphila melanogaster $EITALIC".image("drosophilandi_F.png", width=>343, height=>277, tex_size=>700).$BR." ".$BR.image("drosophilandi_M.png", width=>320, height=>355, tex_size=>700).$BR."  ".$BR.image("drosophilandi_C.png", width=>213, height=>250, tex_size=>700).$BR."All images courtesy of www.drosphilandi.com".$BR.$BR.$ECENTER;

TEXT(ColumnTable($column1,$column2));



########


 
BEGIN_PGML 
 
 

  *Notation Notes:*  
Using a combination of popup displays and numerical entries, present your answers in the form laid out in the row table below. Adjust the real world variable of bristle counts according to the English description for an inclusive inequality and the correct continuity correction. Please note that the "popup" answers used in webwork cannot accommodate the inclusive inequalities of [`\le`] and [`\ge`]. These inequalities will have to be expressed with [`(< or =)`] and [`(> or =)`]. For example, if given the question "Find the probability of drawing a fruit fly that has a total bristle count between [$ax] and [$bx] bristles (inclusive) on the dorsal surface of the fruit fly's mesothorax." Then your answer would take the following form:  
 
 
END_PGML

BEGIN_TEXT

$PAR

\{
DataTable(
[
[['Use the real world variable, \(Y\), within probability notation as your first translation from text to notation:'],' \(P( $ax \le   Y  \le  $bx ) \) '],
[[' Adjust the bounded regions using inclusive inequalities and the continuity correction.  ' ],' \(\approx P( $axcc \le  Y   \le  $bxcc )  \)'],
[[ 'Convert these adjusted real world values into the closest second decimal Z-scores.'], '  \( \approx P( $zxl2 \le  Z  \le  $zxu2 )  \)'],
[[ 'Calculate the probability with fourth decimal accuracy:'],' \(\approx $px \)']
], 
caption => 'Adjust the real-world values for the continuity correction and then use Z-score probability notations to calculate the probability.',
midrules => 1,
align => '|p{5in}|p{1.8in}| ',
);
\}
$PAR


END_TEXT

#Adapted weighted answers values:  

## Problem 8.2a ##
WEIGHTED_ANS( ($popup1)->cmp, 2 );
WEIGHTED_ANS( ($popup2)->cmp, 2 );
WEIGHTED_ANS( ($ans1)->cmp, 7 );
WEIGHTED_ANS( ($popup3)->cmp, 2 );
WEIGHTED_ANS( ($ans2)->cmp, 7 );
WEIGHTED_ANS( ($ans3)->cmp, 8 );

## Problem 8.2b  ##
WEIGHTED_ANS( ($popup4)->cmp, 2 );
WEIGHTED_ANS( ($popup5)->cmp, 7 );
WEIGHTED_ANS( ($popup6)->cmp, 2 );
WEIGHTED_ANS( ($popup7)->cmp, 7 );
WEIGHTED_ANS( ($ans4)->cmp, 7 );
WEIGHTED_ANS( ($popup8)->cmp, 2 );
WEIGHTED_ANS( ($ans5)->cmp, 7 );
WEIGHTED_ANS( ($ans6)->cmp, 7 );

## Problem 8.2c  ##
WEIGHTED_ANS( ($popup9)->cmp, 2 );
WEIGHTED_ANS( ($popup10)->cmp, 7 );
WEIGHTED_ANS( ($popup11)->cmp, 2 );
WEIGHTED_ANS( ($popup12)->cmp, 7 );
WEIGHTED_ANS( ($ans7)->cmp, 7 );
WEIGHTED_ANS( ($popup13)->cmp, 2 );
WEIGHTED_ANS( ($ans8)->cmp, 7 );
WEIGHTED_ANS( ($ans9)->cmp, 7 );


BEGIN_PGML_SOLUTION


The correct answers are coming....in 2017, Hah!

Without context:
zb1 = [$zb1] 
zb2 = [$zb2]  

With context at second decimal:
zzb1 = [$zzb1] 
zzb2 = [$zzb2]  

tes1 = [$tes1]  
test1 = [$test1]  


dif = [$dif]  
dif5 = [$dif5]  

zb22 = [$zb22]  

zb = [$zb]  

ans5 = [$ans5]  
ans6 = [$ans6]  

END_PGML_SOLUTION

ENDDOCUMENT();        
In reply to tim Payer

Re: Sprint rounding convention fails

by Davide Cervone -
This is probably due to the fact that the numbers are stored in base 2, not base 10. Note that numbers like 2.105 can't be accurately represented as (floating point) binary numbers, since .1 is a repeating "decimal" in binary (it is .00011001100110011... in binary). So any fixed-size binary representation will not be fully accurate. For example, if you print the result of 2.105 - 2.1 you will get 0.00499999999999989 not .005 as expected.

This means that edge case rounding (like 2.105) can be inaccurate, and will depend on exactly where the truncation of the representation occurs. Not only is the representation not accurate, but the algorithm used by sprint to decide how to round will include tests against other numbers, also represented in binary, and those tests will not be perfect because of the truncation of the numbers.

The upshot is that there is very little you can do about this. It is inherent in the nature of how real numbers are stored in computers, and it just comes with the territory. Your best bet is to make your initial values so that they avoid the situation where a digit of 5 has to be rounded.
In reply to tim Payer

Re: Sprint rounding convention fails

by Alex Jordan -
I *think* the following would work. Untested.

If you load the Fraction context, and feed these perl decimal reals into Fraction(), as in:

Fraction(2.105 -1.105);

Then you will create the equivalent of Fraction(1,1). Which you could then convert to a Real, and it would be equivalent to Real(1).

This is an awkward way to address this issue, but the reason (I think) it would work is that when you pass a perl real $x to Fraction (as opposed to a string or a numerator,denominator array), then a continued fraction algorithm is applied to $x. The continued fraction algorithm in use has a parameter in place that is supposed to address the potential for rounding error to cause havoc. And my reading of the algorithm in this case is that it would terminate on about the first recursive run, and return Fraction(1,1). I could be wrong, since I haven't taken the time to try.

Either you are asking for:
Fraction(1.00.....00d)
in which case the algorithm extracts the 1, and quits because 0.00.....00d's reciprocal is too large;

or you are asking for:
Fraction(0.99.....99d)
in which case the algorithm will start by taking the reciprocal, to get
1.00.....00e
and basically do the first case then.

The principle is still that instead of rounding, the continued fraction algorithm is looking for a fraction as close as possible to what $x was (above or below) while imposing an upper bound on the denominator.
In reply to Alex Jordan

Re: Sprint rounding convention fails

by Alex Jordan -
What I posted before is probably overkill. If you write these particular problems to do the subtraction at the integer level (2105-1105) and then divide at the end by the appropriate power of 10, that might be a cleaner way to avoid machine rounding errors.