Return the Fractional part of a number

General FreeBASIC programming questions.
Rainwulf
Posts: 35
Joined: Mar 28, 2007 11:33

Return the Fractional part of a number

Postby Rainwulf » Sep 17, 2009 12:38

I need the opposite of int...

I need to get the fraction part of a number, like everything after the decimal point.

For example.
Given 5.123

It returns ".123"

Is there a function that does that already? I am looking in the help file.

I hacked a function together that uses mod, but its not the best.

fractional=((number*1000) MOD 1000)/1000

As well, floor and ceiling would be great as well.
RayBritton
Posts: 306
Joined: Jun 02, 2005 7:11
Contact:

Postby RayBritton » Sep 17, 2009 12:59

FRAC does that.

Code: Select all

print frac(5.123)


displays 0.123

http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgFrac


For the other functions:

Code: Select all

Int(i)


where i is between 0 and 0.999 inclusive, returns the floor and add 1 to get the ceiling.

http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgInt
Last edited by RayBritton on Sep 17, 2009 13:07, edited 3 times in total.
fxm
Posts: 9916
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Postby fxm » Sep 17, 2009 13:03

You can use this formula which runs for positive or negative values :
fractional = number - Sgn(number) * Int(Sgn(number) * number)
counting_pine
Site Admin
Posts: 6225
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Postby counting_pine » Sep 18, 2009 20:38

It should be noted that frac() is the counterpart of fix() - i.e. frac(x) = x - fix(x). Because fix() truncates rather than rounds down, this results in non-positive results for negative numbers.

If you want the result always to be non-negative, you should explicitly do "x - int(x)" instead. (int() always rounds down, so obviously the difference can never be negative.)

To get the ceiling, you should do "-int(-x)". Don't just add 1 to int() because that will mean whole numbers get rounded up - e.g. int(3) + 1 = 4.
owen
Posts: 554
Joined: Apr 19, 2006 10:55
Location: Kissimmee, FL
Contact:

mymod

Postby owen » May 22, 2010 17:10

n mod m : the result is an integer
n mod m + frac(n): using FRAC returns an incorrect result
mymod(n,m): seems to work correctly

365.01 Mod 360 = 5
365.01 Mod 360 = 5.009999999999991
365.01 Mod 360 = 5.01

Code: Select all

'$lang: "deprecated"

Declare Function mymod(n As Double,m As Integer) As Double

Function mymod(n As Double,m As Integer) As Double
   Select Case n
      Case 0
   mymod = 0
      Case Else
   If Instr(Str(n),".")<>0 Then
      mymod=Val(Str(n Mod m)+"."+Mid(Str(n),Instr(Str(n),".")+1))
   Else
      mymod = (n Mod m)
   EndIf
   End Select
End Function

Dim As Double n
Dim As Integer m
n=365.01
m=360
Print n;" Mod";m;" ="; n Mod m
Print n;" Mod";m;" ="; n Mod m + Frac(n)
Print n;" Mod";m;" ="; mymod(n,m)
Sleep
End

' 365.01 Mod 360 = 5
' 365.01 Mod 360 = 5.009999999999991
' 365.01 Mod 360 = 5.01
 
owen
Posts: 554
Joined: Apr 19, 2006 10:55
Location: Kissimmee, FL
Contact:

oops forgot about mod rounding

Postby owen » May 22, 2010 21:11

298.7967216637024 mod 360 is 299 because 298.7xx is rounded up
so naturally mymod function (above post) didn't work.

this is better

Code: Select all

Function mymod(n As Double,m As Integer) As Double
   Select Case n
      Case 0
         mymod = 0
      Case Else
         If InStr(Str(n),".")<>0 Then
            mymod=Val(Str(val(Mid(Str(n),1,InStr(Str(n),".")-1)) Mod m)+"."+Mid(Str(n),InStr(Str(n),".")+1))
         Else
            mymod = (n Mod m)
         EndIf
   End Select
End Function

counting_pine
Site Admin
Posts: 6225
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: mymod

Postby counting_pine » May 23, 2010 8:21

owen wrote:n mod m : the result is an integer
n mod m + frac(n): using FRAC returns an incorrect result
That's as correct as floating-point can get.
Powers of 1/10 cannot be stored accurately in binary, just as powers of 1/3 cant be stored in decimal. For example:

1/300 is 0.0033333 in decimal with 5 digits precision.
1 + 1/300 is 1.0033 in the same format.
If you take 1 away from that you get 0.0033000.

So it looks like the math was wrong, but the math was actually accurate. It's just that it was working on a number that wasn't exactly what you want it to be.

In your trick, str() is giving you the imprecise number .009999..., but to fewer places (because the part before the decimal point takes up more of the significant digits), so it looks accurate. When you pass it back to val(), it fudges the end digits so the final decimal looks as close as it can to .01.

Usually the binary digits can be fudged enough with val() so that the decimal expansion looks like the decimal number you want, but it's not always guaranteed, e.g.

Code: Select all

dim i as integer, s as string
for i = 0 to 100
    s = "1e-" & i
    print s, val(s)
next
owen
Posts: 554
Joined: Apr 19, 2006 10:55
Location: Kissimmee, FL
Contact:

using variable as double

Postby owen » May 23, 2010 12:54

thanx for the post
if my goal is to get a result of 5.0 to 5.9 into the variable called result what's my best choice?

Code: Select all

Dim As Double n,result
Dim As Integer m
m=360
For n = 365.0 To 365.9 Step .1
   result = n Mod m + Frac(n)
   Print result
Next
Sleep
End

the results range from 5.0 to 6.8
counting_pine
Site Admin
Posts: 6225
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Postby counting_pine » May 24, 2010 9:12

One solution might be to use integer math and store everything as tenths of degrees. So a full circle would be 3600.
To ensure it prints correctly without using Print Using or Format you'll probably want to use Str() on the integer and manually insert the decimal point.

Code: Select all

dim i as integer, s as string
for i = 0 to 99
   
    s = str(i)
    s = left(s, len(i)-1) & "." & right(s, 1)
    if left(s, 1) = "." then s = "0" & s
   
    print s
   
next


I've just checked, and Print simply won't print some double precision values the way you'd want it to, so you can't just print the result of the number divided by 10:

Code: Select all

dim i as integer, s as string
for i = 0 to 999
    s = str(i / 10#)
    if instr(s & ".", ".") + 1 < len(s) then print s
next
Early weak points seem to be values over 8 and values over 64, which last until the number gains an extra decimal digit and the loss of precision falls off the end.

Return to “General”

Who is online

Users browsing this forum: Bing [Bot], srvaldez and 5 guests