0

For example, take this IRB session:

1.9.3-p385 :046 > float = func_that_creates_large_float
=> 1.5815450433041317e+51 
1.9.3-p385 :047 > float.to_i
=> 1581545043304131697954259018410479150921451567054848

Can I be sure that the integer returned to me represents the same value as the float? I ask mainly because the Ruby documentation describes the to_i method on the Float class as "Returns the float truncated to an Integer". The word 'truncated' jumps out at me.

Edit: I think describing my situation would make it clear why I'm asking this. I'm trying to take the cube root of a large integer that I know will result in an integer as well. The cube root function uses a float to do its calculation, giving my result as a float. I need the NON-scientific-notation value and have no idea how to approach this.

Thank you.

hlh
  • 1,992
  • 15
  • 24
  • 1
    Well you're converting a floating point number to an integer. Leaving aside really large values (where there wouldn't be any information at a smaller-than-integer precision anyway) what would you expect it to do for something like 1.4? That's why it's talking about truncation... Note that although I don't know much about Ruby, I don't believe there's any such thing as "a Ruby float in scientific notation" - there are values which are represented using `x.ye+z` when converted to a string... but that's just a text representation, not a fundamental difference between such values and ones like 1.5 – Jon Skeet Aug 03 '13 at 21:03
  • You're right, that makes my question look really silly. In my case the float ends in .0, so would there be no truncation? I stuck 'Ruby' in the title just to target my question; I get your point there. – hlh Aug 03 '13 at 21:12
  • Are you sure that `float` really is a float? What is the result of `func_that_creates_large_float.class` ? – steenslag Aug 03 '13 at 23:46
  • Hi, the class is `Float`. I'm thinking I've asked the wrong question here, I should have posted something like "How to convert from a very large integer, to a float, and back, with no corruption of the value?" – hlh Aug 03 '13 at 23:53

2 Answers2

1

On the contrary, (deterministic but meaningless) data is added (as you demonstrated in your code sample). You're not losing any information. But it's not the same value.

Your float has a precision of about 17 decimal digits, your int suddenly has a precision of 51 digits. This has to do with Floating Point Arithmetic: Issues and Limitations. That "precision" arises as the result of the binary representation of a decimal floating point value, which leads to the "invention" of a series of digits in your integer.

Consider the following (in Python, which follows the same rules, as will probably all programming languages that feature arbitrarily long integers):

>>> a = int(1.5815450433041317e+51)
>>> a
1581545043304131697954259018410479150921451567054848
>>> b = a + 10000000000000000
>>> a == b
False
>>> float(a) == float(b)
True
>>> a == 1.5815450433041317e+51
True

In Python, the decimal module would be quite a good compromise:

>>> from decimal import Decimal, getcontext
>>> a = str(29873409852730498572309485723**3)  # just an arbitrary number
>>> getcontext().prec = 100
>>> cube = Decimal(a)
>>> cube ** Decimal("0.333333333333333333333333333333333333333333333333333333333
33333333333333333333")
Decimal('29873409852730498572309485722.99999999999999999999999999999999999999999
999998041297117811390088120595')

(which still has a slight rounding error due to the problem of representing 1/3 as a decimal float, but the result is obvious, isn't it?)

Tim Pietzcker
  • 328,213
  • 58
  • 503
  • 561
  • Great answer, I didn't know this about floating point numbers. It matters to me that I'm not achieving the same value. I updated my post with my specific situation, are you able to comment on it? – hlh Aug 03 '13 at 21:19
  • You need to use a different approach then. The cube root function will not have a high enough precision - the result will be wrong (as you can see when cubing the result you got). – Tim Pietzcker Aug 03 '13 at 21:21
  • Any suggestions? Is it just impossible to take the cube root of a large integer, then? – hlh Aug 03 '13 at 21:24
  • You'll need to roll your own, perhaps building from something like [this](http://stackoverflow.com/questions/356090/how-to-compute-the-nth-root-of-a-very-big-integer-in-python). – Tim Pietzcker Aug 03 '13 at 21:25
  • @hlh: Does Ruby have a gem like Python's [`decimal` module](http://docs.python.org/2/library/decimal.html)? That might do the trick. – Tim Pietzcker Aug 03 '13 at 21:32
  • I think we might've been reading the same SO post that mentions the decimal module! I'll look around. – hlh Aug 03 '13 at 21:36
  • The ruby version would be the [BigDecimal module](http://www.ruby-doc.org/stdlib-2.0/libdoc/bigdecimal/rdoc/BigDecimal.html) in standard lib. – steenslag Aug 03 '13 at 23:26
1

Obviously, if the number of significant figures is greater than the magnitude of the exponent portion, then you will lose information during float to integer conversion.

Ruby (like many other programming languages) does not have a variable-precision floating point. Instead, the number of significant figures is fixed to a constant Float::DIG, which is 15 by default. That means, if you have a float smaller than 10^15, then you will lose information during float to integer conversion.


Regarding your question about the cubic root within integers, it is better to do the calculation within the Rational class. You don't need to worry about truncation.
(8 ** Rational("1/3")).to_r
# => (2/1)
sawa
  • 165,429
  • 45
  • 277
  • 381
  • Thanks for the suggestion, I gave the rationals a try and it still truncated my output unfortunately. Gave the same result as the scientific-notation float when I call .to_i on it. – hlh Aug 03 '13 at 22:15
  • What about changing `Float::DIG` to a larger number? If it does not work, then maybe Ruby is not the language for your need. Ruby is not good for numerical calculation. – sawa Aug 03 '13 at 22:16