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.

It's time to register for SAS Innovate! Join your SAS user peers in Las Vegas on April 16-19 2024.
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

It's time to register for SAS Innovate! Join your SAS user peers in Las Vegas on April 16-19 2024.

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

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