DOUBLE's fractional digits to ULONGINT ?

General FreeBASIC programming questions.
cbruce
Posts: 165
Joined: Sep 12, 2007 19:13
Location: Dallas, Texas

DOUBLE's fractional digits to ULONGINT ?

Post by cbruce »

Is there a math way to exactly translate a DOUBLE's fractional digits to a ULONGINT?

NOTE:
  • I do not want to convert a DOUBLE to a ULONGINT !!!
    I want to translate a DOUBLE's fractional digits to a ULONGINT.
    e.g. 0.876399937 ==> 876399937ull
I can get there with an intermediate STRING assignment - but that is a lot of overhead.

I can get there by multiplying a DOUBLE by a large ULONGINT CONST... but I would have to use a STRING version of the DOUBLE's fractional digits to get the length so that I could use the correct CONST for that set of digits. Because, without being able to choose the correct CONST for each set of digits, I sometimes end up with ULONGINT results that are too large - or sometimes with rounding errors. [see output examples below].

Code: Select all

dim d as double
dim dsu as ulongint
dim dmu as ulongint
'
print "Translate a DOUBLE's fractional digits to ULONGINT representation:"
print
print "1. RND() DOUBLE"
print "2. DOUBLE fractional digits to STRING to ULONGINT"
print "3. DOUBLE Multiplied by CONST to ULONGINT"
print
For i = 1 To 30
    'RND() DOUBLE
    d = RND()
    'DOUBLE to STRING to ULONGINT - EXACT representation of the DOUBLE digits.
    dsu = CULngInt(mid(str(d), 3))
    'DOUBLE Multiplied by CONST to ULONGINT.
    '   Sometimes an EXACT representation of the DOUBLE digits.
    '   Sometimes a ROUNDED representation of the DOUBLE digits.
    '   Sometimes 10 to 100 times greater than a representation of the DOUBLE digits.
    'NOTE:
    '   Attempted with various CONST values.
    '   Some result is always off.
    dmu = d * 10000000000000000ull
    '
    print tab(9); d
    print tab(12); dsu
    print tab(12); dmu
    print
next

Code: Select all

' Output examples:

'1. RND() DOUBLE
'2. DOUBLE fractional digits to STRING to ULONGINT
'3. DOUBLE Multiplied by CONST to ULONGINT

' 0.5324827746953815
'   5324827746953815
'   5324827746953815     'same

' 0.2956116099376231
'   2956116099376231
'   2956116099376232     'rounded up

' 0.6646097381599248
'   6646097381599248
'   6646097381599247     'rounded down

' 0.179675575112924
'   179675575112924
'   1796755751129240     'x10

' 0.55096355965361
'   55096355965361
'   5509635596536100     'x100
srvaldez
Posts: 3379
Joined: Sep 25, 2005 21:54

Re: DOUBLE's fractional digits to ULONGINT ?

Post by srvaldez »

I understand your question but I don't see an easy solution, what's the reason for you wanting to do this?
perhaps there's a different approach to your problem.
cbruce
Posts: 165
Joined: Sep 12, 2007 19:13
Location: Dallas, Texas

Re: DOUBLE's fractional digits to ULONGINT ?

Post by cbruce »

I want to be able to generate a specified range of random doubles - not just the [0 to 1) range that a standard RND() that generates doubles does - but a range like [0.283834 to 0.9875922245]. And I haven't been able to figure out how to do that using only doubles.

The only way I could think of to do it is to translate the two doubles' digits to ulongints - generate a random value between the range of those ulongint values (which is easy to do) - and then convert the generated ulongint random value to a double (which is easy and fast to do).

With a RND() that generates ulongints, the example above would become RND(283834ull, 9875922245ull) - then you divide the ulongint result by &hffffffffffffffffull to create the resulting double.

Note: I want the RND() range parameters to actually be the doubles that the user wants to work with. I want to hide all of this manipulation inside the RND() function itself. The user just says RND(0.283834, 0.9875922245) and gets back their correct double result.
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: DOUBLE's fractional digits to ULONGINT ?

Post by jj2007 »

The fpu has instructions that can isolate the fraction, but I can't get it to work in FB (Error: invalid use of register):

Code: Select all

    d = RND()
    asm fld d
    asm fld st
    asm frndint
    asm fsubp
    asm fistp dfu
It works fine in another Basic dialect, but is this the output you want?

Code: Select all

0.6017979375239967265   601797937523996726
0.3345390753814613219   334539075381461321
0.3800352918740010830   380035291874001083
0.8916944829824631807   891694482982463180
0.7454095696121832009   745409569612183200
0.8613964000800220859   861396400080022085
0.4389618751709885696   438961875170988569
0.8379560746431650630   837956074643165062
0.3001870129073617420   300187012907361741
0.6476385100438799591   647638510043879959
0.6796988126985414523   679698812698541452
0.5406018366139318508   540601836613931850
0.6898235518123229888   689823551812322988
0.7596592812478851525   759659281247885152
0.4607612095810701042   460761209581070104
0.3866698474829880176   386669847482988017
0.6984820022040093753   698482002204009375
0.6082141741995020575   608214174199502057
0.7680263217374531297   768026321737453129
0.9728959023642265679   972895902364226567
Source:

Code: Select all

include \masm32\MasmBasic\MasmBasic.inc
  SetGlobals dest:REAL10, dfu:QWORD, factor:QWORD=1000000000000000000
  Init
  FpuSet MbTrunc64
  For_ ct=0 To 19
	Rand(0.283834, 0.9875922245, dest)
	Print Str$("\n%Jf\t", dest)
	fld dest
	fld st
	frndint
	fsub
	fild factor
	fmul
	fistp dfu
	Print Str$("%i", dfu)
  Next 
EndOfCode
Note that even with such a high precision (REAL10 is long double), and the high multiplication factor, there are always rounding errors. This is by design; if needed, you can tickle out one more digit if your upper random number lies below 0.92233720368547758.
paul doe
Moderator
Posts: 1733
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: DOUBLE's fractional digits to ULONGINT ?

Post by paul doe »

cbruce wrote:I want to be able to generate a specified range of random doubles - not just the [0 to 1) range that a standard RND() that generates doubles does - but a range like [0.283834 to 0.9875922245]. And I haven't been able to figure out how to do that using only doubles.
Could this be of any help?

Code: Select all

function rndRange( byval min as double, byval max as double ) as double
  return( rnd() * ( max - min ) + min )
end function

randomize()

do while( inkey() = "" )
	? rndRange( 0.283834, 0.9875922245 )
loop
cbruce
Posts: 165
Joined: Sep 12, 2007 19:13
Location: Dallas, Texas

Re: DOUBLE's fractional digits to ULONGINT ?

Post by cbruce »

Perfect Paul! With that, I only need to convert my internal rng's ulongint result to a double and shove it into the equation. Simple and fast. A sliding range window... obvious once you see it. [smile]
Thanks!
paul doe
Moderator
Posts: 1733
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: DOUBLE's fractional digits to ULONGINT ?

Post by paul doe »

cbruce wrote:...A sliding range window... obvious once you see it. [smile]
Thanks!
You're welcome. The formula is simply a linear interpolation of the value returned by rnd(). Glad to help.
srvaldez
Posts: 3379
Joined: Sep 25, 2005 21:54

Re: DOUBLE's fractional digits to ULONGINT ?

Post by srvaldez »

even though Rnd returns a double I am not sure of the precision of the number returned
FB Manual wrote:With the -lang fb dialect, a 32 bit Mersenne Twister function with a granularity of 32 bits is used.
see also viewtopic.php?f=3&t=26587
srvaldez
Posts: 3379
Joined: Sep 25, 2005 21:54

Re: DOUBLE's fractional digits to ULONGINT ?

Post by srvaldez »

jj2007 wrote:The fpu has instructions that can isolate the fraction, but I can't get it to work in FB (Error: invalid use of register):

Code: Select all

    d = RND()
    asm fld d
    asm fld st
    asm frndint
    asm fsubp
    asm fistp dfu

Code: Select all

dim shared dest as double, dfu as ulongint, factor as ulongint=1000000000000000000ull

For ct as long=0 To 19
	dest=Rnd
	Print dest,
	asm
		fld qword ptr [dest]
		fld st(0)
		frndint
		fsubp st(1)
		fild qword ptr [factor]
		fmulp st(1)
		fistp qword ptr [dfu]
	end asm
	Print dfu
Next 
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: DOUBLE's fractional digits to ULONGINT ?

Post by jj2007 »

Thanks, no more build errors. But the results do not convince me. Is ulongint = QWORD?
srvaldez
Posts: 3379
Joined: Sep 25, 2005 21:54

Re: DOUBLE's fractional digits to ULONGINT ?

Post by srvaldez »

jj2007 wrote:But the results do not convince me. Is ulongint = QWORD?
of course, a longint is 64-bit
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: DOUBLE's fractional digits to ULONGINT ?

Post by dodicat »

If you map rnd to ten trillion (ten quintillion over the pond) then it looks like:

Code: Select all




Function map(a As Double,b As Double,x As Double,c As Double,d As Double) As Ulongint
    Return ((d)-(c))*((x)-(a))/((b)-(a))+(c)
End Function

#define U(x) culngint(x*10000000000000000000)

For z As Long=1 To 50
    Var x=Rnd
    Print Tab(12); x
    Print Tab(15);map(0,1,x,0,10000000000000000000)
    Print Tab(15);U(x)
    Print
Next
Sleep



  
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: DOUBLE's fractional digits to ULONGINT ?

Post by jj2007 »

OK, it works:

Code: Select all

dim shared dest as double, dfu as ulongint, factor as ulongint=1000000000000000000ull

asm
  push 3967
  fclex
  fldcw word ptr [esp]
  pop edx
end asm	

For ct as long=0 To 19
   dest=Rnd
   Print dest,
   asm
      fld qword ptr [dest]
      fld st(0)
      frndint
      fsubp st(1)
      fild qword ptr [factor]
      fmulp st(1)
      fistp qword ptr [dfu]
   end asm
   Print dfu
Next
Sleep

Code: Select all

 0.3300995409954339         330099540995433926
 0.3290384791325778         329038479132577776
 0.5324827746953815         532482774695381522
 0.7599449595436454         759944959543645381
 0.6424803049303591         642480304930359125
 0.6527107947040349         652710794704034924
 0.2956116099376231         295611609937623143
 0.970114589901641          970114589901641011
 0.9696021103300154         969602110330015420
 0.9504357760306448         950435776030644774
 0.5354314723517746         535431472351774573
 0.6804801726248115         680480172624811530
 0.07139793620444834        71397936204448342
 0.6646097381599248         664609738159924745
 0.0247647250071168         24764725007116794
 0.4909006857778877         490900685777887701
 0.03945830371230841        39458303712308406
 0.4660093509592116         466009350959211587
 0.8267167930025607         826716793002560734
 0.8180722289253026         818072228925302624
cbruce
Posts: 165
Joined: Sep 12, 2007 19:13
Location: Dallas, Texas

Re: DOUBLE's fractional digits to ULONGINT ?

Post by cbruce »

Although I don't need this anymore, you are missing one of the errors that I found in this attempt to multiply a double by a ulongint constant to get an exact representation of the doubles fractional digits as a ulongint value.

The constant must be the correct value in relation to the length of the number of digits in the double's fractional component - or else the resulting ulongint will either have too few or too many digits.

From your example of using a const of 1000000000000000000ull ... none of these are correct - (in fact most of them are wrong):

Code: Select all

0.5324827746953815         532482774695381522
0.9696021103300154         96960211033001542
0.6804801726248115         680480172624811530
So you would have to count the digits in the fractional component in order to figure out which const value to multiply by.

That's why I had ended up working with strings - in order to get the exact representation of the digits of the fractional component.

Example:
If I was looking for a max range double of 0.6804801726248115 - and I ended up with a max range ulongint of 680480172624811530 to use in the random range calculation - I could easily end up with a random ulongint, that when translated back to a double, would be a larger value than the max double requested, etc.
integer
Posts: 408
Joined: Feb 01, 2007 16:54
Location: usa

Re: DOUBLE's fractional digits to ULONGINT ?

Post by integer »

cbruce wrote:I want to be able to generate a specified range of random doubles - not just the [0 to 1) range that a standard RND() that generates doubles does - but a range like [0.283834 to 0.9875922245]. And I haven't been able to figure out how to do that using only doubles.

The only way I could think of to do it is to translate the two doubles' digits to ulongints - generate a random value between the range of those ulongint values (which is easy to do) - and then convert the generated ulongint random value to a double (which is easy and fast to do).

With a RND() that generates ulongints, the example above would become RND(283834ull, 9875922245ull) - then you divide the ulongint result by &hffffffffffffffffull to create the resulting double.

Note: I want the RND() range parameters to actually be the doubles that the user wants to work with. I want to hide all of this manipulation inside the RND() function itself. The user just says RND(0.283834, 0.9875922245) and gets back their correct double result.

Code: Select all

function GetRandomValueBetweenLimits(byval LowValue as double, byval HiValue as double ) as double
    dim as double range = Hivalue - LowValue
    return (LowValue + rnd * range )
end function

''driver

    dim as double Lo, Hi, SomeWhereBetwixt
    randomize
    print " First#",," Between'em",," 2ndNumber"
    for i as long = 1 to 10
        Lo = rnd
        Hi = rnd
        SomeWhereBetwixt = GetRandomValueBetweenLimits(Lo, Hi)
        print Lo, SomeWhereBetwixt, Hi
    next i
    getkey
I believe dodicat posted the algo for this.
Post Reply