Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instantiating from floats #32

Closed
kevindecapite opened this issue Jan 28, 2020 · 5 comments
Closed

Instantiating from floats #32

kevindecapite opened this issue Jan 28, 2020 · 5 comments

Comments

@kevindecapite
Copy link

kevindecapite commented Jan 28, 2020

The docs/readme states:

Note about floating-point values: instantiating from a float might be unsafe, as floating-point values are imprecise by design, and could result in a loss of information. Always prefer instantiating from a string, which supports an unlimited number of digits...

I'm looking to implement this library throughout my existing application, but am using float types in my method signatures. According to the above, it sounds like I should either pass in strings instead, or convert the floats to strings.

So one of?

BigDecimal::of(strval($x));
BigDecimal::of((string) $x);

EDIT: I should mention that the floats I'm working with are provided from another library. In other words, I am not creating these values myself, but instead performing calculations on generated ones.

@BenMorel
Copy link
Member

BenMorel commented Jan 28, 2020

Hi 👋

I apologize if the README is not clear enough on this point. The suggestion really is to not use floats at all, as you'll most likely be bitten at some point by a rounding issue.

Floats can accurately represent integers (up to a point), halves, quarters, etc. Pretty much everything else is an approximation, that may or may not give the value you expect when converted to a string.

Example from this IEEE 754 converter:

You entered: 2.5
Value actually stored in float: 2.5

You entered: 2.4
Value actually stored in float: 2.400000095367431640625

As you can see though, (string) 2.4 in PHP still returns '2.4'. PHP is usually good at converting the value to the string you expect. But you can't be 100% confident that this will always be the case.

This is why the README advocates to deal with integer or string values everywhere outside BigNumber.

Back to your question.

In your case, you have a float value. Converting it to a string using (string) or strval() will not change anything, as this is what of() does internally. It will just fool of() into thinking that it was given a precise value.

What you should really do is refactor your codebase to only ever deal with integers or strings. That is, if you care about precision (when dealing with monetary values, for example).

EDIT: I should mention that the floats I'm working with are provided from another library. In other words, I am not creating these values myself, but instead performing calculations on generated ones.

If you really care about precision, you should not be using said library. Floating point values are inherently approximations in mose cases, and are not there for this purpose.

If you want to deal with large values but do not care about precision, then it may be OK to use the library in question. But then I would question why you'd need brick/math at all; if precision is not a requirement, you may perform calculations on floats directly.

If you give me the name of the library in question, I can probably better clarify this point.

Can we improve the README?

If there's some changes in wording I can make to the README to make this more clear, please let me know!

@BenMorel BenMorel changed the title How to use the factory. Instantiating from floats Jan 28, 2020
@BenMorel BenMorel pinned this issue Jan 28, 2020
@kevindecapite
Copy link
Author

Thank you for your thorough and helpful reply :-)

The library I'm using is this one:

https://github.com/cyjoelchen/php-sweph

Which is a PHP extension of this C library:

https://www.astro.com/swisseph/swephinfo_e.htm

The underlying C lib uses double, which may be different from a float, although I'll gladly show my ignorance there.

As for overall precision, I would like my codebase to maintain the same level of precision as the Swiss Ephemeris library.

@BenMorel
Copy link
Member

Thanks for the link!

PHP's floats are double under the hood, so they're the same thing (technically, usually float is 32-bit and double is 64-bit, aka double precision, but in PHP there's a single type, float, that's always 64-bit).

Your library deals with planetary positions, so inherently approximative. Floating-point values are a very good fit for this. Therefore I stand by my statement above, you may not need brick/math at all?

The only advantage of using brick/math to perform further calculations on your floating point values, is that you won't accumulate imprecision. At the cost of speed!

@kevindecapite
Copy link
Author

accumulate imprecision.

That's my concern because I do perform calculations on the returned data. Even though, as you stated, the source data is approximate anyway, I still want to avoid moving further away from those approximations by using the float primitive.

Do floats always approximate consistently? I would think they must, but this is part of my hesitation in using them and why I'm drawn to brick/math instead.

@BenMorel
Copy link
Member

Do floats always approximate consistently?

You're getting me out of my comfort zone ;-)

I would suggest you try the same set of computations with both double and BigDecimal, measure the difference, and decide if the imprecision is within acceptable bounds!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants