How to get nextFloat() from SplittableRandom? - java

I've been using java's SplittableRandom ever since I heard about it, due to its speed, and not being in need of multithreading. However, although it has almost every method from Random class, it doesn't come with nextFloat(). Why's that?
Now, the real question is, how would I then go about creating that nextFloat method? Seeing the double is generated as follows: (from JDK 8)
final double internalNextDouble(final double n, final double n2) {
double longBitsToDouble = (this.nextLong() >>> 11) * 1.1102230246251565E-16;
if (n < n2) {
longBitsToDouble = longBitsToDouble * (n2 - n) + n;
if (longBitsToDouble >= n2) {
longBitsToDouble = Double.longBitsToDouble(Double.doubleToLongBits(n2) - 1L);
}
}
return longBitsToDouble;
}
.. I was hoping that I could just turn it to a 32-bit number generation with the following;
final float internalNextFloat(final float min, final float max) {
float intBitsToFloat = (this.nextInt() >>> 11) * 1.1102230246251565E-16f;
if (min < max) {
intBitsToFloat = intBitsToFloat * (max - min) + min;
if (intBitsToFloat >= max) {
intBitsToFloat = Float.intBitsToFloat(Float.floatToIntBits(max) - 1);
}
}
return intBitsToFloat;
}
However, this returns 0.000000. I can only assume it overflows somewhere, in which case I'm pretty sure the problem lies at the following line:
(this.nextInt() >>> 11) * 1.1102230246251565E-16f;
So, not being experienced with shifting (and using epsilon I guess), how could I achieve what I want?

Without having thought about the mathematics of this too deeply, it seems to me that you could just use the nextDouble method to generate a double within the desired range and then cast the result to float.

You need to first understand the meaning behind this line:
double longBitsToDouble = (this.nextLong() >>> 11) * 1.1102230246251565E-16;
this.nextLong() returns a 64 long.
>>> 11 turn the long to unsigned and removes the last 11 bits, so now we get a 53-bit random value. This is also the precision of double.
* 1.1102230246251565E-16. This is equivalent to 1 / 9007199254740992.0, or 2-53.
So longBitsToDouble is a randomly uniform double from 0 (inclusive) to 1 (exclusive).
Compared with a float, its precision is 24 bits, while this.nextInt() generates a 32-bit random value, so the corresponding line should be written as
float intBitsToFloat = (this.nextInt() >>> 8) * 5.960464477539063E-8f;
(Instead of the decimal representation 5.960464477539063E-8f you could also use hexadecimal float, which may be clearer to readers:
float intBitsToFloat = (this.nextInt() >>> 8) * 0x1.0p-24;
)

Related

Java: Generating a random double within a range (inclusive of the min and max to the range)

I need to generate a random double between -0.10 and 0.25 inclusive of -0.10 and 0.25. I don't completely understand random number generators with java yet so I'm not quite sure how to do this. I know that the code below generates a number within that range, but I'm almost 100% certain it's not inclusive yet. How would I change it to be inclusive of the min and max of the range?
public double randDouble() {
//formula: (Math.random() * (max - min) + min
//in this case, min = -0.10 and max = 0.25
return (Math.random() * 0.35) - 0.10;
}
My question is different than the one #javaguy is talking about because no where on that thread does someone say how to make this inclusive on both ends. And I've tested this code, but haven't seen an output of -.10 or 0.25 so unless my tests just weren't big enough, I can't see how it's inclusive of both ends.
Since you want values between -0.10 and 0.25 both inclusive, i would suggest a different approach to control the granularity of your random values. Using Math.random() will return values between 0.0(inclusive) and 1.0(exclusive), so using your approach you will never get the value of 0.25. Instead use the Random() class and use integers to get both ends inclusive.
Random randomValue = new Random();
int randomInt = randomValue.nextInt(3501) - 1000; //Will get integer values between -1000 and 2500
Double rangedValue = Double(randomInt)/10000.0 // Get values between -0.10 and 0.25
Alternatively you can more decimal places by increasing the magnitude of the values passed into randomValue.nextInt() and accordingly altering the value being devided by in the last line.
Your plan to obtain random double values inclusive of the limits is ill-conceived, since there is no guarantee that you will ever receive a value which is equal to either limit.
That's due to the huge precision of double, which means that the possibility of obtaining any exact given random double is astronomically slim.
You can have that random number issued billions of times, and you may get thousands of values that are very close, extremely close to the limit, and yet none of them may ever happen to be equal to the limit.
Therefore, you cannot have any logic that depends on a random double number being issued which will be equal to the limit, because that random number may never be yielded.
So, the solution to your problem is very simple: stop worrying about inclusive vs. exclusive, because all you can ever hope for is exclusive. That should make things more simple for you.
Your current formula includes the lower bound but excludes the upper bound. To fix this, you can add Double.MIN_VALUE to the upper bound, creating a new max. This slightly changes your bounds so that you actually want to exclude the new max. Yout old max is included in [min, newMax).
public double randDouble() {
//formula: Math.random() * (max + Double.MIN_VALUE - min) + min
//in this case, min = -0.10 and max = 0.25
return (Math.random() * (0.35 + Double.MIN_VALUE)) - 0.10;
}
If you can live with float and with ~8 million steps, you can simply throw away almost half of the numbers (one less than half of the numbers):
private static Random rnd=new Random();
public static float next0to1() {
float f;
do {
f=rnd.nextFloat();
} while(f>0.5);
return f*2;
}
this function will generate random floats between 0 and 1, both ends included.
A test snippet like
long start=System.currentTimeMillis();
int tries=0;
while(next0to1()!=0)
tries++;
System.out.println(tries);
while(next0to1()!=1)
tries++;
System.out.println(tries);
System.out.println(System.currentTimeMillis()-start);
or a longer one with your actual numbers and some extra checks
long start=System.currentTimeMillis();
int tries=0;
float min=-0.1f;
float max=0.25f;
tries=0;
float current;
do {
tries++;
current=min+next0to1()*(max-min);
if(current<min)
throw new RuntimeException(current+"<"+min);
if(current>max)
throw new RuntimeException(current+">"+max);
} while(current!=min);
System.out.println(tries);
do {
tries++;
current=min+next0to1()*(max-min);
if(current<min)
throw new RuntimeException(current+"<"+min);
if(current>max)
throw new RuntimeException(current+">"+max);
} while(current!=max);
System.out.println(tries);
System.out.println(System.currentTimeMillis()-start);
will typically show a couple ten million tries to generate both a 0 and 1, completes in less than a second for me on a 5-year old laptop.
Side remark: it's normal that it usually takes more than 8 million tries: while nextFloat() generates 24 bits, which is reduced to ~23 by throwing away almost half of the numbers, the generator itself works on 48 bits.
The best you can do with Random is still nextInt() as shown in Sasang's answer. The usable range is 2^30:
static double next0to1() {
return rnd.nextInt(0x40000001)/(double)0x40000000;
}
This one takes far more time (minutes) and tries (billions, tries is better changed to long for this one), to generate both 0 and 1.
Random.nextDouble(), or "cracking" random
nextDouble() needs more precision than what the generator can produce in a single step and it puts together a 26 and 27-bit number instead:
public double nextDouble() {
return (((long)next(26) << 27) + next(27))
/ (double)(1L << 53);
}
where next() is described as
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
return (int)(seed >>> (48 - bits))
(but it can be actually found too, like https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/Random.java#L183)
which implies that in order to generate 0, a a=next(26) and a consecutive b=next(27) have to both return 0, so
seeda=00000000 00000000 00000000 00xxxxxx xxxxxxxx xxxxxxxx (binary, 48 bits)
seedb=00000000 00000000 00000000 000xxxxx xxxxxxxx xxxxxxxx (binary, 48 bits)
and from the update:
seedb = (seeda * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
it can be brute-forced in a moment (4 million possibilities) what seeda has to be:
long mask = ((long) ((1 << 27) - 1)) << 21;
System.out.println(Long.toBinaryString(mask));
for (long i = 0; i < 1 << 22; i++)
if (((i * 0x5DEECE66DL + 0xBL) & mask) == 0)
System.out.println(i);
where the loop runs in the lower 22 bits (as we know that the rest is zero) and mask is 11111111 11111111 11111111 11100000 00000000 00000000 for checking if the relevant 27 bits of the next seed are zeros.
So seeda=0.
The next question is if there exists a previous seedx to generate seeda, so
seeda = (seedx * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
just this time we don't know the bits, so brute-forcing won't help. But this kind of equation is an actual thing, called a congruence relation, and is solvable.
0 "=" seedx * 0x5DEECE66DL + 0xBL (mod 2^48)
WolframAlpha needs it in the form of Ax "=" B (mod C), and in decimal, so the inputs are
25214903917x <- 0x5DEECE66D
281474976710656 <- 2^48
-11 <- 0xB, went to the other side
One possible solution is 107048004364969. Learning/knowing that Random XOR-s the seed with the magic number, it can be tested:
double low=-0.1,
high=0.25;
Random rnd=new Random(0x5DEECE66DL ^ 107048004364969L);
System.out.println(low+(high-low)*rnd.nextDouble()==low);
will result in true. So yes, Random.nextDouble() can generate an exact 0.
The next part is 0.5:
seeda=10000000 00000000 00000000 00xxxxxx xxxxxxxx xxxxxxxx (binary, 48 bits)
seedb=00000000 00000000 00000000 000xxxxx xxxxxxxx xxxxxxxx (binary, 48 bits)
seeda has its 48th digit set to 1.
Comes the brute-forcing loop:
long mask = ((long) ((1 << 27) - 1)) << 21;
System.out.println(Long.toBinaryString(mask));
for (long i = 0; i < 1 << 22; i++)
if ((((i + 0x800000000000L) * 0x5DEECE66DL + 0xBL) & mask) == 0)
System.out.println(i);
Ooops, there is no solution. 5DEECE66D has its lowest bit set (it's an odd number), so when we have exactly one bit set to 1 as in 0x80...., that will remain 1 after the multiplication - and of course this also applies if we try moving that single bit to the right).
TL;DR: Random.nextDouble() (and consequently Math.random()) will never generate an exact 0.5. (or 0.25, 0.125, etc.)
As i run in a similar problem getting some random float values in between a min and a max and found no solution ready to use, I would like to post mine. Thank You Sasang for providing me with the right idea!
The min and max values are inclusive but if the range or precision is big it's unlikely to happen. You could use the precision also as a parameter if you have different use cases for that function.
public class MyRandom {
private static Random randomValue = new Random();
public static double randomFloat(double min, double max) {
double precision = 1000000D;
double number = randomValue.nextInt((int) ((max - min) * precision + 1)) + min * precision;
return number / precision;
}
}
example:
called with min = -0.10 and max 0.25 and precision 1000000 generated output like:
-0.116965
0.067249
0.246948
-0.180695
-0.033533
0.08214
-0.053864
0.216388
-0.158086
0.05963
0.168015
0.119533
You can also use Streams but is [fromInclusive, toExclusive).
Check the following sample, one using Streams and another one using Random:
Edited: (I wasn't careful reading the docs, all versions are fromInclusive-toExclusive)
public class MainClass {
public static void main(String[] args) {
generateWithStreams(-0.10, 0.25);
generateWithoutStreams(-0.10, 0.25);
}
private static void generateWithStreams(double fromInclusive, double toExclusive) {
new Random()
.doubles(fromInclusive, toExclusive)
// limit the stream to 100 doubles
.limit(100)
.forEach(System.out::println);
}
private static void generateWithoutStreams(double fromInclusive, double toExcusive) {
Random random = new Random();
// generating 100 doubles
for (int index = 0; index < 100; index++) {
System.out.println(random.nextDouble() * (toExcusive - fromInclusive) + fromInclusive);
}
}
}
The formula you used will give you random values inclusive of lower bound of range but exclusive of upper bound, because:
Math.random() returns a pseudorandom double greater than or equal to 0.0 and less than 1.0

Java integer division doesn't give floor for negative numbers

I was trying to use java's integer division, and it supposedly takes the floor. However, it rounds towards zero instead of the floor.
public class Main {
public static void main(String[] args) {
System.out.println(-1 / 100); // should be -1, but is 0
System.out.println(Math.floor(-1d/100d)); // correct
}
}
The problem is that I do not want to convert to a double/float because it needs to be efficient. I'm trying to solve this with a method, floorDivide(long a, long b). What I have is:
static long floorDivide(long a, long b) {
if (a < 0) {
// what do I put here?
}
return a / b;
}
How can I do this without a double/float?
floorDiv() from Java.Math that does exactly what you want.
static long floorDiv(long x, long y)
Returns the largest (closest to positive infinity) long value that is less than or equal to the algebraic quotient.
Take the absolute value, divide it, multiply it by -1.
Weird bug.
You can use
int i = (int) Math.round(doubleValToRound);
It will return a double value that you can cast into an int without lost of precission and without performance problems (casts haven't a great computational cost)
Also it's equivalent to
int a = (int) (doubleValToRound + 0.5);
//in your case
System.out.println((int) ((-1 / 100) + 0.5));
With this last one you won't have to enter into tedious and unnecesary "if" instructions. Like a good suit, its valid for every moment and has a higher portability for other languages.
This is ugly, but meets the requirement to not use a double/float. Really you should just cast it to a double.
The logic here is take the floor of a negative result from the integer division if it doesn't divide evenly.
static long floorDivide(long a, long b)
{
if(a % b != 0 && ((a < 0 && b > 0) || (a > 0 && b < 0)))
{
return (a / b - 1);
}
else
{
return (a / b);
}
}
Just divide the two integers. then add -1 to the result (in case the absolute value of both numerator and denominator are not same). For example -3/3 gives you -1, the right answer without adding -1 to the division.
Since a bit late, but you need to convert your parameters to long or double
int result = (int) Math.floor( (double) -1 / 5 );
// result == -1
This worked for me elegantly.
I would use floorDiv() for a general case, as Frank Harper suggested.
Note, however, that when the divisor is a power of 2, the division is often substituted by a right shift by an appropriate number of bits, i.e.
x / d
is the same as
x >> p
when p = 0,1,...,30 (or 0,1,...,62 for longs), d = 2p and x is non-negative. This is not only more effective than ordinary division but gives the right result (in mathematical sense) when x is negative.

Simple and Quick way to calculate the hamming distance of a binary integer to 0?

I'm writing a Sudoku solver and I have to calculate the what I learned was called the hamming distance of an int to 0, e.g. the hamming distance of 7 (111 in binary) to 0 is 3. So I simply do:
for(int dist = 0 ; num != 0 ; num>>=1) dist += (num&1);
Although that works fine, I find it a bit clumsy. I tried to come up with a binary operation trick to calculate the distance (mostly for fun), but I could only find a way that works for a distance of 1:
(num-1) ^ ((num<<1)-1) == num → true only if hamming dist to 0 == 1
I looked on StackOverflow and on the Net, but I couldn't find anything.
Assuming that num is never negative and always smaller than 512, is there a nicer/more elegant way to evaluate it, perhaps some binary operations tricks? If not, given the assumptions above, is there an approximation of the hamming distance that would always be within an error < 1?
To create a lookup table for 9 bits (since this is for Sudoku):
int dist[512];
dist[0] = 0;
for (i=1; i<512; i++)
dist[i] = (i&1) + dist[i/2];
To avoid the initial calculation this could also be written as a memoizing recursive function.
int dist(int bits) {
static _dist[512] = {};
if (bits == 0)
return 0;
else if (_dist[bits] == 0)
_dist[bits] = (bits & 1) + dist(bits/2);
return _dist[bits];
In java you can use the static method Integer.bitCount(int i)
If you need it in another language, this is the java source which should be pretty strait forward to translate.
/**
* Returns the number of one-bits in the two's complement binary
* representation of the specified {#code int} value. This function is
* sometimes referred to as the <i>population count</i>.
*
* #param i the value whose bits are to be counted
* #return the number of one-bits in the two's complement binary
* representation of the specified {#code int} value.
* #since 1.5
*/
public static int bitCount(int i) {
// HD, Figure 5-2
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
Not sure, if this helps, but out of curiosity I implemented it via templates:
template <int N>
struct Hamming {
enum { value = Hamming< (N/2) >::value + (N&1)};
};
template <>
struct Hamming<0>
{
enum { value = 0 };
};
int main() {
std::cout << Hamming<7>::value << std::endl;
return 0;
}
It can only be used if N is known at compile time, thus I think you will have to use something else in your case. However, it nicely demonstrates how any computation at runtime can (in principle) be completely avoided.

Recover the original number from a float

Numbers are being stored in a database (out of my control) as floats/doubles etc.
When I pull them out they are damaged - for example 0.1 will come out (when formatted) as 0.100000001490116119384765625.
Is there a reliable way to recover these numbers?
I have tried new BigDecimal(((Number) o).doubleValue()) and BigDecimal.valueOf(((Number) o).doubleValue()) but these do not work. I still get the damaged result.
I am aware that I could make assumptions on the number of decimal places and round them but this will break for numbers that are deliberately 0.33333333333 for example.
Is there a simple method that will work for most rationals?
I suppose I am asking is there a simple way of finding the most minimal rational number that is within a small delta of a float number?.
you can store the numbers in the database as String and on the retrieval just parseDouble() them. This way the number wont be damaged, it will be same as you store there.
is there a simple way of finding a rational number that is within 0.00001 of a float number?.
This is called rounding.
double d = ((Number) o).doubleValue();
double d2 = Math.round(d * 1e5) / 1e5;
BigDecimal bd = BigDecimal.valueOf(d2);
or you can use BigDecimal to perform the rounding (I avoid using BigDecimal as it is needelessly slow once you know how to use rounding of doubles)
double d = ((Number) o).doubleValue();
BigDecimal bd = BigDecimal.valueOf(d).setScale(5, RoundingMode.HALF_UP);
Note: never use new BigDecimal(double) unless you understand what it does. Most likely BigDecial.valueOf(double) is what you wanted.
Here's the bludgeon way I have done it - I would welcome a more elegant solution.
I chose an implementation of Rational that had a mediant method ready-made for me.
I refactored it to use long instead of int and then added:
// Default delta to apply.
public static final double DELTA = 0.000001;
public static Rational valueOf(double dbl) {
return valueOf(dbl, DELTA);
}
// Create a good rational for the value within the delta supplied.
public static Rational valueOf(double dbl, double delta) {
// Primary checks.
if ( delta <= 0.0 ) {
throw new IllegalArgumentException("Delta must be > 0.0");
}
// Remove the integral part.
long integral = (long) Math.floor(dbl);
dbl -= integral;
// The value we are looking for.
final Rational d = new Rational((long) ((dbl) / delta), (long) (1 / delta));
// Min value = d - delta.
final Rational min = new Rational((long) ((dbl - delta) / delta), (long) (1 / delta));
// Max value = d + delta.
final Rational max = new Rational((long) ((dbl + delta) / delta), (long) (1 / delta));
// Start the fairey sequence.
Rational l = ZERO;
Rational h = ONE;
Rational found = null;
// Keep slicing until we arrive within the delta range.
do {
// Either between min and max -> found it.
if (found == null && min.compareTo(l) <= 0 && max.compareTo(l) >= 0) {
found = l;
}
if (found == null && min.compareTo(h) <= 0 && max.compareTo(h) >= 0) {
found = h;
}
if (found == null) {
// Make the mediant.
Rational m = mediant(l, h);
// Replace either l or h with mediant.
if (m.compareTo(d) < 0) {
l = m;
} else {
h = m;
}
}
} while (found == null);
// Bring back the sign and the integral.
if (integral != 0) {
found = found.plus(new Rational(integral, 1));
}
// That's me.
return found;
}
public BigDecimal toBigDecimal() {
// Do it to just 4 decimal places.
return toBigDecimal(4);
}
public BigDecimal toBigDecimal(int digits) {
// Do it to n decimal places.
return new BigDecimal(num).divide(new BigDecimal(den), digits, RoundingMode.DOWN).stripTrailingZeros();
}
Essentially - the algorithm starts with a range of 0-1. At each iteration I check to see if either end of the range falls between my d-delta - d+delta range. If it does we've found an answer.
If no answer is found we take the mediant of the two limits and replace one of the limits with it. The limit to replace is chosen to ensure the limits surround d at all times.
This is essentially doing a binary-chop search between 0 and 1 to find the first rational that falls within the desired range.
Mathematically I climb down the Stern-Brocot Tree choosing the branch that keeps me enclosing the desired number until I fall into the desired delta.
NB: I have not finished my testing but it certainly finds 1/10 for my input of 0.100000001490116119384765625 and 1/3 for 1.0/3.0 and the classic 355/113 for π.

Format Double as Fraction [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 7 years ago.
Improve this question
Is there a library that will convert a Double to a String with the whole number, followed by a fraction?
For example
1.125 = 1 1/8
I am only looking for fractions to a 64th of an inch.
Your problem is pretty simple, because you're assured the denominator will always divide 64. in C# (someone feel free to translate a Java version):
string ToMixedFraction(decimal x)
{
int whole = (int) x;
int denominator = 64;
int numerator = (int)( (x - whole) * denominator );
if (numerator == 0)
{
return whole.ToString();
}
while ( numerator % 2 == 0 ) // simplify fraction
{
numerator /= 2;
denominator /=2;
}
return string.Format("{0} {1}/{2}", whole, numerator, denominator);
}
Bonus: Code Golf
public static string ToMixedFraction(decimal x) {
int w = (int)x,
n = (int)(x * 64) % 64,
a = n & -n;
return w + (n == 0 ? "" : " " + n / a + "/" + 64 / a);
}
One problem you might run into is that not all fractional values can be represented by doubles. Even some values that look simple, like 0.1. Now on with the pseudocode algorithm. You would probably be best off determining the number of 64ths of an inch, but dividing the decimal portion by 0.015625. After that, you can reduce your fraction to the lowest common denominator. However, since you state inches, you may not want to use the smallest common denominator, but rather only values for which inches are usually represented, 2,4,8,16,32,64.
One thing to point out however, is that since you are using inches, if the values are all proper fractions of an inch, with a denominator of 2,4,8,16,32,64 then the value should never contain floating point errors, because the denominator is always a power of 2. However if your dataset had a value of .1 inch in there, then you would start to run into problems.
How about org.apache.commons.math ? They have a Fraction class that takes a double.
http://commons.apache.org/math/api-1.2/org/apache/commons/math/fraction/Fraction.html
You should be able to extend it and give it functionality for the 64th. And you can also add a toString that will easily print out the whole number part of the fraction for you.
Fraction(double value, int
maxDenominator) Create a fraction
given the double value and maximum
denominator.
I don't necessarily agree, base on the fact that Milhous wants to cover inches up to 1/64"
Suppose that the program demands 1/64" precision at all times, that should take up 6 bits of the mantissa. In a float, there's 24-6 = 18, which (if my math is right), should mean that he's got a range of +/- 262144 + 63/64"
That might be enough precision in the float to convert properly into the faction without loss.
And since most people working on inches uses denominator of powers of 2, it should be fine.
But back to the original question, I don't know any libraries that would do that.
Function for this in a C-variant called LPC follows. Some notes:
Addition to input value at beginning is to try to cope with precision issues that otherwise love to wind up telling you that 5 is 4 999999/1000000.
The to_int() function truncates to integer.
Language has a to_string() that will turn some floats into exponential notation.
string strfrac(float frac) {
int main = to_int(frac + frac / 1000000.0);
string out = to_string(main);
float rem = frac - to_float(main);
string rep;
if(rem > 0 && (to_int(rep = to_string(rem)) || member(rep, 'e') == Null)) {
int array primes = ({ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 });
string base;
int exp;
int num;
int div;
if(sscanf(rep, "%se%d", base, exp) == 2) {
num = to_int(replace(base, ".", ""));
div = to_int(pow(10, abs(exp)));
} else {
rep = rep[2..];
num = to_int(rep);
div = to_int(pow(10, strlen(rep)));
}
foreach(int prime : primes) {
if(prime > num)
break;
while((num / prime) * prime == num && (div / prime) * prime == div) {
num /= prime;
div /= prime;
}
}
out += " " + num + "/" + div;
}
return out;
}
i wrote this for my project i hope it could be usefull:
//How to "Convert" double to fraction("a/b") - kevinlopez#unitec.edu
private boolean isInt(double number){
if(number%2==0 ||(number+1)%2==0){
return true;
}
return false;
}
private String doubleToFraction(double doub){
//we get the whole part
int whole = (int)doub;
//we get the rest
double rest = doub - (double)whole;
int numerator=1,denominator=1;
//if the whole part of the number is greater than 0
//we'll try to transform the rest of the number to an Integer
//by multiplying the number until it become an integer
if(whole >=1){
for(int i = 2; ; i++){
/*when we find the "Integer" number(it'll be the numerator)
* we also found the denominator(i,which is the number that transforms the number to integer)
* For example if we have the number = 2.5 when it is multiplied by 2
* now it's 5 and it's integer, now we have the numerator(the number (2.5)*i(2) = 5)
* and the denominator i = 2
*/
if(isInt(rest*(double)i)){
numerator = (int)(rest*(double)i);
denominator = i;
break;
}
if(i>10000){
//if i is greater than 10000 it's posible that the number is irrational
//and it can't be represented as a fractional number
return doub+"";
}
}
//if we have the number 3.5 the whole part is 3 then we have the rest represented in fraction 0.5 = 1/2
//so we have a mixed fraction 3+1/2 = 7/2
numerator = (whole*denominator)+numerator;
}else{
//If not we'll try to transform the original number to an integer
//with the same process
for(int i = 2; ; i++){
if(isInt(doub*(double)i)){
numerator = (int)(doub*(double)i);
denominator = i;
break;
}
if(i>10000){
return doub+"";
}
}
}
return numerator+"/"+denominator;
}
My code looks like this.
public static int gcd(int a, int b)
{
if (b == 0)
return a;
else
return gcd(b, a % b);
}
public static String doubleToStringFraction(Double d)
{
StringBuffer result = new StringBuffer(" " + ((int) Math.floor(d)));
int whole = (int) ((d - Math.floor(d)) * 10000);
int gcd = gcd(whole, 10000);
result.append(" " + (whole / gcd) + "/" + 10000 / gcd + " ");
return result.toString();
}
As several others have poited out, fractions of 64 can be precicely represented by IEEE-floats. This means we can also convert to a fraction by moving and masking bits.
This is not the place to explain all details of floating point representations, please refer to wikipedia for details.
Briefly: a floating point number is stored as (sign)(exp)(frac) where sign is 1 bit, exp is 11 bits and frac is the fraction part (after 1.) and is 52 bits. This is enterpreted as the number:
(sign == 1 ? -1 : 1) * 1.(frac) * 2^(exp-1023)
Thus, we can get the 64th by moving the point accoring to the exponent and masking out the 6 bits after the point. In Java:
private static final long MANTISSA_FRAC_BITMAP = 0xfffffffffffffl;
private static final long MANTISSA_IMPLICIT_PREFIX = 0x10000000000000l;
private static final long DENOM_BITMAP = 0x3f; // 1/64
private static final long DENOM_LEN = 6;
private static final int FRAC_LEN = 52;
public String floatAsFrac64(double d) {
long bitmap = Double.doubleToLongBits(d);
long mantissa = bitmap & MANTISSA_FRAC_BITMAP | MANTISSA_IMPLICIT_PREFIX;
long exponent = ((bitmap >> FRAC_LEN) & 0x7ff) - 1023;
boolean negative = (bitmap & (1l << 63)) > 0;
// algorithm:
// d is stored as SE(11)F(52), implicit "1." before F
// move point to the right <exponent> bits to the right:
if(exponent > FRAC_LEN) System.out.println("warning: loosing precision, too high exponent");
int pointPlace = FRAC_LEN-(int)exponent;
// get the whole part as the number left of the point:
long whole = mantissa >> pointPlace;
// get the frac part as the 6 first bits right of the point:
long frac = (mantissa >> (pointPlace-DENOM_LEN)) & DENOM_BITMAP;
// if the last operation shifted 1s out to the right, we lost precision, check with
// if any of these bits are set:
if((mantissa & ((MANTISSA_FRAC_BITMAP | MANTISSA_IMPLICIT_PREFIX) >> (pointPlace - DENOM_LEN))) > 0) {
System.out.println("warning: precision of input is smaller than 1/64");
}
if(frac == 0) return String.format("%d", whole);
int denom = 64;
// test last bit, divide nom and demon by 1 if not 1
while((frac & 1) == 0) {
frac = frac >> 1;
denom = denom >> 1;
}
return String.format("%d %d/%d", whole, frac, denom);
}
(this code can probably be made shorter, but reading bit-flipping-code like this is hard enough as it is...)
I create simply Fraction library.
The library is available here: https://github.com/adamjak/Fractions
Example:
String s = "1.125";
Fraction f1 = Fraction.tryParse(s);
f1.toString(); // return 9/8
Double d = 2.58;
Fraction f2 = Fraction.createFraction(d);
f2.divide(f1).toString() // return 172/75 (2.29)
To solve this problem (in one of my projects), I took the following steps:
Built a dictionary of decimal/fraction strings.
Wrote a function to search the dictionary for the closest matching fraction depending on the "decimal" part of the number and the matching criteria.

Categories

Resources