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

Hi Friends,

 

I ran below code

data y;
cnt = 2.55;
a = cnt-int(cnt);
d = (a>0.550);
e = (a=0.550);
f = (a < 0.550);
run;

 

f comes out as 1, which indicates that sas is considering  0.55 as less then 0.55. Any thoughts

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
ChrisNZ
Tourmaline | Level 20

@PaigeMiller >And I'm still not convinced that round is a solution for all possible floating point numbers.

My experience is that rounding always works if done appropriately. Since the error is always very small, rounding by say a billionth gets rid of it.

To reuse @Rick_SAS 's code:

data A;
  X = 0.00005;
  Y = 0.00005 - 1e-16; 
  A=X=Y; 

  RndX = round(X, 1e-9);
  RndY = round(Y, 1e-9);

  B=RndX=RndY; 
  C=RndY=0.00005;

  putlog A= B= C=;
run;

A=0 B=1 C=1

Though I agree than in principle the decimal number cannot be represented, at least rounding correctly homogenises the binary values.

 

View solution in original post

25 REPLIES 25
ketpt42
Quartz | Level 8

Here's the documentation of the INT function: https://documentation.sas.com/?docsetId=lefunctionsref&docsetTarget=p1b1zd0wuyufp0n1jbqlr27p4eay.htm...

 

The result is fuzzed, so this is expected behavior. Using INTZ will produce the results you are expecting.

 

data y;
cnt = 2.55;
a = cnt-intz(cnt);
d = (a>0.550);
e = (a=0.550);
f = (a < 0.550);
run;
 
SP_SAS
Obsidian | Level 7

same problem with intz too.

PaigeMiller
Diamond | Level 26

@ketpt42 wrote:

Here's the documentation of the INT function: https://documentation.sas.com/?docsetId=lefunctionsref&docsetTarget=p1b1zd0wuyufp0n1jbqlr27p4eay.htm...

 

The result is fuzzed, so this is expected behavior. Using INTZ will produce the results you are expecting.

 

data y;
cnt = 2.55;
a = cnt-intz(cnt);
d = (a>0.550);
e = (a=0.550);
f = (a < 0.550);
run;
 

I don't see how INTZ helps here. Also, on my computer, when I run the above code, I get f=1.

--
Paige Miller
Tom
Super User Tom
Super User

Welcome to the world of storing decimal numbers using binary numbers. 0.55 and 2.55 cannot be exactly represented by binary numbers.

data y;
cnt = 2.55;
a = cnt-int(cnt);
diff = (0.55-a);
d = (a>0.550);
e = (a=0.550);
f = (a < 0.550);
run;

proc print; run;
Obs     cnt      a        diff       d    e    f

 1     2.55    0.55    2.2204E-16    0    0    1

 

Kurt_Bremser
Super User

It has nothing to do with the INT() function, but with the way binary math works in calculations:

data _null_;
x1 = 2.55 - 2;
x2 = (x1 = 0.55);
put x1= x2=;
run;

Log:

73         data _null_;
 74         x1 = 2.55 - 2;
 75         x2 = (x1 = 0.55);
 76         put x1= x2=;
 77         run;
 
 x1=0.55 x2=0
SP_SAS
Obsidian | Level 7

Here is the best answer for my purpose.. let me know if you guys think anything can go wrong with this solution.

data y;
cnt = 2.550;
intaa = int(cnt);
intzz = intz(cnt);
a = cat('0.',scan(cnt,2,'.')) *1;
d = (a>0.550);
e = (a=0.550);
f = (a < 0.550);
run;

PaigeMiller
Diamond | Level 26

@SP_SAS wrote:

Here is the best answer for my purpose.. let me know if you guys think anything can go wrong with this solution.

data y;
cnt = 2.550;
intaa = int(cnt);
intzz = intz(cnt);
a = cat('0.',scan(cnt,2,'.')) *1;
d = (a>0.550);
e = (a=0.550);
f = (a < 0.550);
run;


I think things can go wrong. Here's code that I think I can trust:

 

data y;
cnt = 2.550;
a = cnt-int(cnt);

d = fuzz(a-0.550)>0;
e = fuzz(a-0.550)=0;
f = fuzz(a-0.550)<0;
run;
--
Paige Miller
Tom
Super User Tom
Super User

You are not addressing the actual issue.  If you only want to compare to 4 decimal places then round the numbers.  Might want to round to 4 decimal digits and then test.

want = round(have,0.0001);

See this paper: https://www.pharmasug.org/proceedings/2014/CC/PharmaSUG-2014-CC50.pdf

Tom
Super User Tom
Super User

The MOD() seems to fix this. Not sure if it does for all values or just this specific one.

The real solution is to ROUND() the value(s) before comparing.

542   data y;
543     cnt = 2.55;
544     a = cnt-int(cnt);
545     b = mod(cnt,1);
546     c = round(a,0.0001);
547     a_test = (a=0.550);
548     b_test = (b=0.550);
549     c_test = (c=0.550);
550     put (_all_) (=/);
551   run;


cnt=2.55
a=0.55
b=0.55
c=0.55
a_test=0
b_test=1
c_test=1
PaigeMiller
Diamond | Level 26

The real solution is to ROUND() the value(s) before comparing.

 

I'm not convinced. Numbers rounded as above to 4 digits cannot be represented exactly in binary. So couldn't there be cases where we're back to square one, where the binary representation causes the problem at the start of this thread?

--
Paige Miller
Tom
Super User Tom
Super User

I don't think SAS has an almost equal operator.  So you need to round, at some point, before comparing.  You could include it in the code that does the comparison.

if round(a/b,0.0001) = round(c/d,0.0001) then do;

@PaigeMiller wrote:

The real solution is to ROUND() the value(s) before comparing.

 

I'm not convinced. Numbers rounded as above to 4 digits cannot be represented exactly in binary. So couldn't there be cases where we're back to square one, where the binary representation causes the problem at the start of this thread?


 

PaigeMiller
Diamond | Level 26

@Tom wrote:

I don't think SAS has an almost equal operator.  So you need to round, at some point, before comparing.  You could include it in the code that does the comparison.

if round(a/b,0.0001) = round(c/d,0.0001) then do;


I quibble with your statement that you "need to round". There are other choices. The FUZZ function, as I pointed out earlier, or the COMPFUZZ function (as pointed out by @Rick_SAS) seem to address the issue, and both functions are indeed, in layman's terms, an "almost equal operator".

 

And I'm still not convinced that round is a solution for all possible floating point numbers.

--
Paige Miller
Kurt_Bremser
Super User

@PaigeMiller wrote:

 

And I'm still not convinced that round is a solution for all possible floating point numbers.


If both values in the comparison are rounded to the same digit, and they differ by a sufficiently small epsilon, the resulting binary "error" will be identical, so the comparison will work.

Rick_SAS
SAS Super FREQ

Kurt, I haven't seen explicit code from you, so perhaps I am misunderstanding, but I think in principle Paige is correct. If the two numbers are on opposite sides of a cutpoint, the rounding will take them in opposite directions.

 

data A;
epsilon = 1e-16;
x = 0.00005;
y = x - epsilon;
RndX = round(x, 0.0001);
RndY = round(y, 0.0001);
put RndX=;
put RndY=;
run;

sas-innovate-2024.png

Don't miss out on SAS Innovate - Register now for the FREE Livestream!

Can't make it to Vegas? No problem! Watch our general sessions LIVE or on-demand starting April 17th. Hear from SAS execs, best-selling author Adam Grant, Hot Ones host Sean Evans, top tech journalist Kara Swisher, AI expert Cassie Kozyrkov, and the mind-blowing dance crew iLuminate! Plus, get access to over 20 breakout sessions.

 

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 25 replies
  • 1145 views
  • 21 likes
  • 8 in conversation