BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
Maplefin
Obsidian | Level 7

Hi, I recently find an issue that SAS may not get the correct answer for decimals calculation. My code is as below:

data x;
a=5.7;
b=4.75;
c=(a-b)/b;
if c=0.2 then flag="Y";
run;

As you can see, I want get the proportion of (a-b) to b. It should be 0.2, and it seems that SAS give the right answer. But to my surprise, the value of variabel Flag is equal to "Y", which means c is not strictly equal to 0.2. In fact, c is over 0.2. Can someone explain it to me? I really want to know the mechanism behind it. 

1 ACCEPTED SOLUTION

Accepted Solutions
andreas_lds
Jade | Level 19

Just have a look at https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/lepg/p0dv87zb3bnse6n1mqo360be70qr.htm

- the sections stating with "Floating-Point" should explain the problem.

View solution in original post

7 REPLIES 7
andreas_lds
Jade | Level 19

Just have a look at https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/lepg/p0dv87zb3bnse6n1mqo360be70qr.htm

- the sections stating with "Floating-Point" should explain the problem.

Ksharp
Super User

That is because computer is unable to store 0.2 exactly , same to other languages Java, Python, R.........

You need to round it before comparing it .

 

data x;
a=5.7;
b=4.75;
c=(a-b)/b;
/*if round(c,1e-12)=0.2 then flag="Y";*/
if round(c,0.000000000001)=0.2 then flag="Y";
run;

 

Maplefin
Obsidian | Level 7
Thanks, to round the decimals may be the good solution.
FreelanceReinh
Jade | Level 19

Hi @Maplefin,

 

Glad to see you found the practical ROUND solution to your issue.

 

So this is yet another example where numeric representation error and arithmetic calculations with numbers of limited precision entail tiny differences between the computer's results (not SAS-specific) and the results expected from using the decimal system. It's a particularly nice example, so let's take a look at the internal workings:

 

First, note that your starting values a and b look quite different in the binary system:

a=101.1011001100110...
b=100.11

 

While b=4.75=19/4 has an exact, finite binary representation using only five binary digits, a=5.7 is a repeating fraction in the binary system: The four-bit pattern "0110" highlighted in green is repeated forever.  As a consequence, an unavoidable numeric representation error (here: 1.776E-16) occurs when 5.7 is rounded to fit into the 64-bit space (actually: 52 mantissa bits + 1 implied bit) available internally:

101.10110011001100110011001100110011001100110011001101

The last, 53rd bit (highlighted in red) has been rounded up and the decimal equivalent of this number is this:

5.70000000000000017763568394002504646778106689453125

 

Thanks to the simplicity of b in the binary system, the subtraction a-b is easy and results in the binary fraction

0.11110011001100110011001100110011001100110011001101000

The rounded bit from above (red) is now the 50th bit, hence more significant than it was before. Indeed, the former numeric representation error has turned into a noticeable rounding error, as the number is slightly larger than the "expected" 0.95:

122   data _null_;
123   if 5.7 - 4.75 > 0.95 then put 'Surprised?';
124   run;

Surprised?

 

Now you will not be surprised that the "red" bit also causes trouble in the division (a-b)/b. This calculation is a bit tedious (if done by hand), yet relatively simple due to the shortness of the binary representation of b. Its result is (after rounding up the 53rd bit):

0.0011001100110011001100110011001100110011001100110011011

which differs (in the least significant bit) from the correctly rounded repeating fraction being the binary equivalent of the decimal 0.2

0.0011001100110011001100110011001100110011001100110011010

 

Here comes the punchline: Ironically, even putting the "exact" numeric literal 0.95 (rather than the deviating a-b) into the division does not yield the perfect result. Instead, the numeric representation error of 0.95 (making this number internally a bit smaller than it should be: 0.9499999999999999555910790149937383830547332763671875) causes a too small result:

0.0011001100110011001100110011001100110011001100110011001

so that, again, an exact equality check in a DATA step would fail:

134   data _null_;
135   if 0.95/4.75 < 0.2  then put 'Surprised again?';
136   run;

Surprised again?

 

Maplefin
Obsidian | Level 7
Great! Thanks for your detailed explanation!

hackathon24-white-horiz.png

2025 SAS Hackathon: There is still time!

Good news: We've extended SAS Hackathon registration until Sept. 12, so you still have time to be part of our biggest event yet – our five-year anniversary!

Register Now

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 7 replies
  • 2547 views
  • 8 likes
  • 5 in conversation