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
@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.
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;
same problem with intz too.
@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.
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
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
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;
@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;
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
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
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?
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?
@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.
@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.
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;
Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!
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.
Ready to level-up your skills? Choose your own adventure.