How to Convert a Fraction to Float in Ruby

how to convert a fraction to float in ruby

Looks like you're going to have to parse the fraction yourself. This will work on fractions and whole numbers, but not mixed numbers (ie: 1½ will not work.)

class String
def to_frac
numerator, denominator = split('/').map(&:to_f)
denominator ||= 1
numerator/denominator
end
end

"1/16".to_frac * 45

How to I render a float as a fraction in ruby on rails

Your question is unclear.

For a start, you need to understand that floating point numbers are imprecise; they have discrete representable values -- For example, it's impossible to represent 0.1 precisely.

This is the cause of the commonly-mislabelled "bug" (in all languages, not just ruby) that:

0.1 + 0.2 == 0.30000000000000004

Secondly, not all numbers are rational. There is no way to represent pi (3.14159...) as a fraction, like you describe.

Thirdly, 12 1/4 is not an equation. An equation must have an equals sign. What you seem to be describing is a mixed number.

And lastly, and you cannot have poorly-defined requirements like "I want to represent 0.33 as 1/3" - i.e. you need to be very explicit about what rounding rules, if any, should be applied. 0.33 is equal to 33/100, not 1/3. So for the purposes of this answer, I'm going to omit any such ill-defined rounding.

Here is how you could programatically try to format mixed numbers in ruby

x = 12.25
puts "#{x.to_i} #{x.to_s.to_r - x.to_i}"
# => "12 1/4"

Or, you could do:

puts x.to_s.to_r.divmod(1).join(' ')
# => "12 1/4"

Note the use of to_s -- that's very much intentional. Without it, you could get "surprising" (but technically correct) results, such as:

0.1.to_r.divmod(1).join(' ')
# => "0 3602879701896397/36028797018963968"

...This is because, as I said at the start, floating point numbers have limited precision. (The number 1/10 cannot be represented in binary.)

If you need to avoid any such rounding errors (for example if you're dealing with currency), you should use BigDecimal instead of Float. (Or maybe Rational, in some circumstances.)

Ruby float to_r calculation. decimals to fractions

This sounds like a typical floating point problem, but hidden inside. While 0.025 may be represented exactly, the function to_r will no doubt perform various floating-point operations internally which are necessarily inexact. The result 3602879701896397/144115188075855872 will no doubt match the intermediate, transformed version of a more closely than your proposal 1/40.

Now 3602879701896397/144115188075855872 is extremely close to being the same as 1/40. But it is not quite equal, so is not simplified.

For more information, look at some of the previous questions related to inexact floating point. This is a nuanced case and a good question therefore, but has its fundamentals in the same things. I'm looking into the ruby C implementation of Float#to_r for more details.

get fraction part of a decimal number

I am not sure if it is the easiest way to do it - but you can simply split the number using "." character - like this:

number = 1.23
parts = number.to_s.split(".")
result = parts.count > 1 ? parts[1].to_s : 0

How to convert floats to human-readable fractions?

I have found David Eppstein's find rational approximation to given real number C code to be exactly what you are asking for. Its based on the theory of continued fractions and very fast and fairly compact.

I have used versions of this customized for specific numerator and denominator limits.

/*
** find rational approximation to given real number
** David Eppstein / UC Irvine / 8 Aug 1993
**
** With corrections from Arno Formella, May 2008
**
** usage: a.out r d
** r is real number to approx
** d is the maximum denominator allowed
**
** based on the theory of continued fractions
** if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
** then best approximation is found by truncating this series
** (with some adjustments in the last term).
**
** Note the fraction can be recovered as the first column of the matrix
** ( a1 1 ) ( a2 1 ) ( a3 1 ) ...
** ( 1 0 ) ( 1 0 ) ( 1 0 )
** Instead of keeping the sequence of continued fraction terms,
** we just keep the last partial product of these matrices.
*/

#include <stdio.h>

main(ac, av)
int ac;
char ** av;
{
double atof();
int atoi();
void exit();

long m[2][2];
double x, startx;
long maxden;
long ai;

/* read command line arguments */
if (ac != 3) {
fprintf(stderr, "usage: %s r d\n",av[0]); // AF: argument missing
exit(1);
}
startx = x = atof(av[1]);
maxden = atoi(av[2]);

/* initialize matrix */
m[0][0] = m[1][1] = 1;
m[0][1] = m[1][0] = 0;

/* loop finding terms until denom gets too big */
while (m[1][0] * ( ai = (long)x ) + m[1][1] <= maxden) {
long t;
t = m[0][0] * ai + m[0][1];
m[0][1] = m[0][0];
m[0][0] = t;
t = m[1][0] * ai + m[1][1];
m[1][1] = m[1][0];
m[1][0] = t;
if(x==(double)ai) break; // AF: division by zero
x = 1/(x - (double) ai);
if(x>(double)0x7FFFFFFF) break; // AF: representation failure
}

/* now remaining x is between 0 and 1/ai */
/* approx as either 0 or 1/m where m is max that will fit in maxden */
/* first try zero */
printf("%ld/%ld, error = %e\n", m[0][0], m[1][0],
startx - ((double) m[0][0] / (double) m[1][0]));

/* now try other possibility */
ai = (maxden - m[1][1]) / m[1][0];
m[0][0] = m[0][0] * ai + m[0][1];
m[1][0] = m[1][0] * ai + m[1][1];
printf("%ld/%ld, error = %e\n", m[0][0], m[1][0],
startx - ((double) m[0][0] / (double) m[1][0]));
}

Convert string to decimal number in ruby

You call to_i, you get integer.

Try calling to_f, you should get a float.

For more string conversion methods, look here.

Converting String 2½ (two and a half) into 2.5

Unicode only supports a small number of vulgar fractions so a simple lookup table will do the trick:

# You might want to double check this mapping
vulgar_to_float = {
"\u00BC" => 1.0 / 4.0,
"\u00BD" => 1.0 / 2.0,
"\u00BE" => 3.0 / 4.0,
"\u2150" => 1.0 / 7.0,
"\u2151" => 1.0 / 9.0,
"\u2152" => 1.0 /10.0,
"\u2153" => 1.0 / 3.0,
"\u2154" => 2.0 / 3.0,
"\u2155" => 1.0 / 5.0,
"\u2156" => 2.0 / 5.0,
"\u2157" => 3.0 / 5.0,
"\u2158" => 4.0 / 5.0,
"\u2159" => 1.0 / 6.0,
"\u215A" => 5.0 / 6.0,
"\u215B" => 1.0 / 8.0,
"\u215C" => 3.0 / 8.0,
"\u215D" => 5.0 / 8.0,
"\u215E" => 7.0 / 8.0,
"\u2189" => 0.0 / 3.0,
}

Then, a little bit of regex wrangling to pull your "number" apart:

s = "2½"
_, int_part, vulgar_part = *s.match(/(\d+)?(\D+)?/)

And finally, put them together taking care to properly deal with possible nils from the regex:

float_version = int_part.to_i + vulgar_to_float[vulgar_part].to_f

Remember that nil.to_i is 0 and nil.to_f is 0.0.

How do you round a float to 2 decimal places in JRuby?

Float#round can take a parameter in Ruby 1.9, not in Ruby 1.8. JRuby defaults to 1.8, but it is capable of running in 1.9 mode.

Converting a floating point to its corresponding bit-segments

The bit data can be exposed via Arrays pack as Float doesn't provide functions internally.

str = [12.125].pack('D').bytes.reverse.map{|n| "%08b" %n }.join
=> "0100000000101000010000000000000000000000000000000000000000000000"

[ str[0], str[1..11], str[12..63] ]
=> ["0", "10000000010", "1000010000000000000000000000000000000000000000000000"]

This is a bit 'around about the houses' to pull it out from a string representation. I'm sure there is a more efficient way to pull the data from the original bytes...


Edit The bit level manipulation tweaked my interest so I had a poke around. To use the operations in Ruby you need to have an Integer so the float requires some more unpacking to convert into a 64 bit int. The big endian/ieee754 documented representation is fairly trivial. The little endian representation I'm not so sure about. It's a little odd, as you are not on complete byte boundaries with an 11 bit exponent and 52 bit mantissa. It's becomes fiddly to pull the bits out and swap them about to get what resembles little endian, and not sure if it's right as I haven't seen any reference to the layout. So the 64 bit value is little endian, I'm not too sure how that applies to the components of the 64bit value until you store them somewhere else, like a 16bit int for the mantissa.

As an example for an 11 bit value from little > big, The kind of thing I was doing was to shift the most significant byte left 3 to the front, then OR with the least significant 3 bits.

v = 0x4F2
((v & 0xFF) << 3) | ( v >> 8 ))

Here it is anyway, hopefully its of some use.

class Float
Float::LITTLE_ENDIAN = [1.0].pack("E") == [1.0].pack("D")

# Returns a sign, exponent and mantissa as integers
def ieee745_binary64
# Build a big end int representation so we can use bit operations
tb = [self].pack('D').unpack('Q>').first

# Check what we are
if Float::LITTLE_ENDIAN
ieee745_binary64_little_endian tb
else
ieee745_binary64_big_endian tb
end
end

# Force a little end calc
def ieee745_binary64_little
ieee745_binary64_little_endian [self].pack('E').unpack('Q>').first
end

# Force a big end calc
def ieee745_binary64_big
ieee745_binary64_big_endian [self].pack('G').unpack('Q>').first
end

# Little
def ieee745_binary64_little_endian big_end_int
#puts "big #{big_end_int.to_s(2)}"
sign = ( big_end_int & 0x80 ) >> 7

exp_a = ( big_end_int & 0x7F ) << 1 # get the last 7 bits, make it more significant
exp_b = ( big_end_int & 0x8000 ) >> 15 # get the 9th bit, to fill the sign gap
exp_c = ( big_end_int & 0x7000 ) >> 4 # get the 10-12th bit to stick on the front
exponent = exp_a | exp_b | exp_c

mant_a = ( big_end_int & 0xFFFFFFFFFFFF0000 ) >> 12 # F000 was taken above
mant_b = ( big_end_int & 0x0000000000000F00 ) >> 8 # F00 was left over
mantissa = mant_a | mant_b

[ sign, exponent, mantissa ]
end

# Big
def ieee745_binary64_big_endian big_end_int
sign = ( big_end_int & 0x8000000000000000 ) >> 63
exponent = ( big_end_int & 0x7FF0000000000000 ) >> 52
mantissa = ( big_end_int & 0x000FFFFFFFFFFFFF ) >> 0

[ sign, exponent, mantissa ]
end
end

and testing...

def printer val, vals
printf "%-15s sign|%01b|\n", val, vals[0]
printf " hex e|%3x| m|%013x|\n", vals[1], vals[2]
printf " bin e|%011b| m|%052b|\n\n", vals[1], vals[2]
end

floats = [ 12.125, -12.125, 1.0/3, -1.0/3, 1.0, -1.0, 1.131313131313, -1.131313131313 ]

floats.each do |v|
printer v, v.ieee745_binary64
printer v, v.ieee745_binary64_big
end

TIL my brain is big endian! You'll note the ints being worked with are both big endian. I failed at bit shifting the other way.



Related Topics



Leave a reply



Submit