Hi guys,
i have a problem. - Im trying to truncate a number by the following procedure:
&result. = int(round(&array.[&counter.],0.000000000000001)*(10**&array.[&counter.-1]))*(10**-&array.[&counter.-1]);
where &counter. is the number which need to be truncated, and &counter.-1 is the number og digits i want to truncate with.
For curtain numbers, this fails. - An example;
int(round(67.1,0.000000000000001)*(10**6))*(10**-6);
which gives 67.09999999.
I have come to the conclusion, that it is the int function, which is the problem;
data _null_;
number = 67.1*100000;
number_int = int(number);
put number;
run;
This returns "6709999" as an example.
Any ideas what to do, to not run into this problem ?
Moved this to for input from folks on SAS functions.
There is nothing wrong with INT() function. By ROUNDING() you have reduced the precision with which the number can be stored. You can see the same effect by reducing the number of storage bytes.
Try this program to see the impact of throwing away some of the precision from a number like one tenth that cannot be represented exactly using binary numbers.
data _null_;
fraction= 67 + 1/10 ;
do trunc=8 to 3 by -1 ;
number=trunc(fraction,trunc);
number_int= int(round(number,0.000000000000001)*(10**6))*(10**-6);
put trunc= number= number_int=;
end;
run;
trunc=8 number=67.1 number_int=67.099999
trunc=7 number=67.1 number_int=67.099999
trunc=6 number=67.1 number_int=67.099999
trunc=5 number=67.099999905 number_int=67.099999
trunc=4 number=67.099975586 number_int=67.099975
trunc=3 number=67.09375 number_int=67.09375
Move your exponents around so that you can round() to an integer.
This:
number_int= int(round(number,0.000000000000001)*(10**6))*(10**-6);
Will become this:
number_int= round(number*(10**15),1) *(10**-15);
Or to make it easier for your formula:
number_int= round(number*(10**(9+6)),1) *(10**(-9-6));
Hi Tom
I have no doubt that this is about precision and binary storage of floating point numbers but I still can't fully understand what's going on here. Can you please try and elaborate/explain a bit more?
In below sample code what I don't understand:
- Why can't I see the difference when printing the numbers using format "32.24"
- Why is there no difference in "case 2"?
data _null_;
/** case1: 6710000 **/
put / '***** case1: 6710000 ******';
number_calc = 67.1 * 100000;
number_fixd = 6710000;
number_diff= number_fixd - number_calc;
put number_calc= 32.24;
put number_fixd = 32.24;
put number_diff = 32.24 /;
put number_calc = binary64.;
put number_fixd = binary64.;
/** case2: 10000 **/
put / '***** case2: 10000 ******';
number_calc = 0.1 * 100000;
number_fixd = 10000;
number_diff= number_fixd - number_calc;
put number_calc= 32.24;
put number_fixd = 32.24;
put number_diff = 32.24 /;
put number_calc = binary64.;
put number_fixd = binary64.;
stop;
run;
***** case1: 6710000 ******
number_calc=6710000.000000000000000000000000
number_fixd=6710000.000000000000000000000000
number_diff=0.000000000931322574615470
number_calc=0100000101011001100110001011101111111111111111111111111111111111
number_fixd=0100000101011001100110001011110000000000000000000000000000000000
***** case2: 10000 ******
number_calc=10000.000000000000000000000000
number_fixd=10000.000000000000000000000000
number_diff=0.000000000000000000000000
number_calc=0100000011000011100010000000000000000000000000000000000000000000
number_fixd=0100000011000011100010000000000000000000000000000000000000000000
Thanks
Patrick
I concur with JakobKrogh that the issue is with the int() function.
data _null_;
number = 67.1*100000;
number_int1 = int(number);
number_int2 = int(number+0.000000001);
number_int3 = int(number+0.0000000001);
number_int4 = round(number,1);
put number= number_int1= / number_int2= number_int3= / number_int4=;
run;
one gets the following:
number=6710000 number_int1=6709999
number_int2=6710000 number_int3=6709999
number_int4=6710000
it turns out that:
int(number+0.0000000005) = 6710000
Nope. Still the same problem. You cannot represent that many distinct decimal digits using binary floating numbers.
Try this one.
data _null_;
number0 = 67.1 * 10**5;
do i=-1 to -10 by -1;
number1 = number0+10**i ;
put i= Z3. +1 number1 =20.12 ;
end;
run;
i=-01 number1=6710000.100000000000
i=-02 number1=6710000.010000000000
i=-03 number1=6710000.001000000000
i=-04 number1=6710000.000100000000
i=-05 number1=6710000.000010000000
i=-06 number1=6710000.000001000000
i=-07 number1=6710000.000000100000
i=-08 number1=6710000.000000010000
i=-09 number1=6710000.000000000000
i=-10 number1=6710000.000000000000
Here's another fun one.
data _null_;
one_tenth=0.1;
one = 1;
do _n_=1 to 10 ; ten_tenths + one_tenth; end;
diff = one - ten_tenths ;
put (_all_) (= best32.);
run;
one_tenth=0.1 one=1 ten_tenths=1 diff=1.1102230246251E-16
Computers are not doing the math in the decimal approach. They are BINARY, thare are 10 ways to understand that are: yes/no.
See the docs SAS(R) 9.4 Language Reference: Concepts, Fourth Edition the decimal value 0.1 is an infinitely repeating reciproke in binary just like 1/3 =0.333... (wat is the last precision?)
Do the math the same in binary just as the processors (it is about that hardware level) are doing.
This fun topic -- numeric precision in SAS -- comes up quite a bit. Jaap is correct: floating point math in computers is not the same as if you worked it out by hand. There is an IEEE standard for how floating point numbers are represented in computers, and SAS follows that standard.
More here:
http://blogs.sas.com/content/sasdummy/2012/03/01/precision-in-sas-numbers/
Also this fun one: what is the difference between 0 and -0?
Chris
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
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.