BookmarkSubscribeRSS Feed
JakobKrogh
Calcite | Level 5

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 ?

9 REPLIES 9
ChrisHemedinger
Community Manager

Moved this to for input from folks on SAS functions.

Learn from the Experts! Check out the huge catalog of free sessions in the Ask the Expert webinar series.
Tom
Super User Tom
Super User

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

Tom
Super User Tom
Super User

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));

Patrick
Opal | Level 21

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

billfish
Quartz | Level 8


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

Tom
Super User Tom
Super User

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

Tom
Super User Tom
Super User

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

jakarman
Barite | Level 11

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.

---->-- ja karman --<-----
ChrisHemedinger
Community Manager

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

Learn from the Experts! Check out the huge catalog of free sessions in the Ask the Expert webinar series.

SAS Innovate 2025: Save the Date

 SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!

Save the date!

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
  • 9 replies
  • 2206 views
  • 0 likes
  • 6 in conversation