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

feature: Fractional Exponents #7

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

CrownedPhoenix
Copy link

No description provided.

@CrownedPhoenix CrownedPhoenix marked this pull request as draft January 10, 2024 22:30
Copy link
Owner

@NeedleInAJayStack NeedleInAJayStack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work!! I love this approach.

Just wondering - have we validated that exponents in a fractional form are sufficient? Like that defining units in a decimal-based format is not required/desired? Also worth noting (although it's probably completely irrelevant), is that integer fractions cannot represent all decimal values: https://en.wikipedia.org/wiki/Irrational_number


var positive: Bool {
switch (numerator, denominator) {
// 0/0 is not positive in this logic

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as an aside, here's a fun math Wikipedia article on this particular fraction: https://en.wikipedia.org/wiki/Indeterminate_form#Indeterminate_form_0/0


extension Fraction: Comparable {
public static func < (lhs: Fraction, rhs: Fraction) -> Bool {
return lhs.numerator < rhs.numerator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that this is correct - it should be:

Suggested change
return lhs.numerator < rhs.numerator
return lhs.asDouble < rhs.asDouble

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I'll fix this. Instead of asDouble I'l just do the int math to create a common denominator and compare them that way.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated this logic.

}

extension SignedInteger {
func over<T: SignedInteger>(_ denominator: T) -> Fraction {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were there type inference issues with using /, like 5.measured(in: .meter.pow(1/2))?

Copy link
Author

@CrownedPhoenix CrownedPhoenix Jan 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. That was what I originally tried.
It was ambiguous to the compiler whether 1 / 2 meant Int / Int or Fraction / Int or Fraction / Fraction etc because Fraction conforms to ExpressibleAsIntegerLiteral.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On that note, how do you feel about | as the operator? It wouldn't be ambiguous.

You could do 2|5 etc

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went ahead and did this - lmk if you like/dislike.

Comment on lines +8 to +14
let gcd = Self.gcd(numerator, denominator)
self.numerator = numerator / gcd
self.denominator = denominator / gcd

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be public?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I guess the tests do a testable import so I didn't catch that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made some more things public

@@ -0,0 +1,163 @@


public struct Fraction: Hashable, Equatable, Sendable {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we provide documentation about this type? Specific areas that would be helpful to document after reading through:

  • init automatically reduces the input fraction
  • String representation uses |

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Will do. I'll make a doc pass for everything I've added.

@@ -7,6 +7,8 @@ final class UnitTests: XCTestCase {
XCTAssertNotEqual(Unit.meter, Unit.foot)
XCTAssertEqual(Unit.meter * Unit.second / Unit.second, Unit.meter)
XCTAssertNotEqual(Unit.newton, Unit.kilogram * Unit.meter / Unit.second.pow(2))
XCTAssertNotEqual(Unit.meter.pow(1.over(2)), Unit.kilogram * Unit.meter / Unit.second.pow(2))
XCTAssertEqual(Unit.meter.pow(1.over(6)), Unit.meter.pow(1.over(2)).pow(1.over(3)) )

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests are great, but could we also add specific tests for the Fraction functions?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Just haven't yet cuz I didn't want to spend more time on it if it wasn't a productive direction to go. I'll make a more robust test suite for the Fraction.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added tests.

@CrownedPhoenix
Copy link
Author

CrownedPhoenix commented Jan 11, 2024

Just wondering - have we validated that exponents in a fractional form are sufficient? Like that defining units in a decimal-based format is not required/desired? Also worth noting (although it's probably completely irrelevant), is that integer fractions cannot represent all decimal values: https://en.wikipedia.org/wiki/Irrational_number

Fractional form is not sufficient. It's just a step in the right direction.
I think after this the next thing I'll work on is some sort of higher level type (maybe an enum) that subsumes both Fraction and Double. If the user enters a Double we assume they are both okay with double precision (as well as its limits) and that they have provided the exact value (at least to the desired significant figures) that they want used.

I haven't thought much about how we'll straddle the operation between ft^(2/5) * s^3.5 kinds of things. I suspect there's some tricky business around that and some decisions to be made.

@CrownedPhoenix CrownedPhoenix force-pushed the feature/fractional-exponents branch from bfa5aac to 53a7872 Compare January 11, 2024 17:59
@NeedleInAJayStack
Copy link
Owner

Fractional form is not sufficient. It's just a step in the right direction. I think after this the next thing I'll work on is some sort of higher level type (maybe an enum) that subsumes both Fraction and Double. If the user enters a Double we assume they are both okay with double precision (as well as its limits) and that they have provided the exact value (at least to the desired significant figures) that they want used.

I haven't thought much about how we'll straddle the operation between ft^(2/5) * s^3.5 kinds of things. I suspect there's some tricky business around that and some decisions to be made.

Oof, do you think we should continue down this direction then or just try and go toward a 'double'-ized exponent? I'd hate to introduce a Fraction concept if we were just going to subsume its functionality with a Double soon.

@CrownedPhoenix CrownedPhoenix force-pushed the feature/fractional-exponents branch from 53a7872 to 03652e7 Compare January 11, 2024 18:29
@CrownedPhoenix
Copy link
Author

CrownedPhoenix commented Jan 11, 2024

Fractional form is not sufficient. It's just a step in the right direction. I think after this the next thing I'll work on is some sort of higher level type (maybe an enum) that subsumes both Fraction and Double. If the user enters a Double we assume they are both okay with double precision (as well as its limits) and that they have provided the exact value (at least to the desired significant figures) that they want used.
I haven't thought much about how we'll straddle the operation between ft^(2/5) * s^3.5 kinds of things. I suspect there's some tricky business around that and some decisions to be made.

Oof, do you think we should continue down this direction then or just try and go toward a 'double'-ized exponent? I'd hate to introduce a Fraction concept if we were just going to subsume its functionality with a Double soon.

Sorry it won't be subsuming the functionality.
Brandon has said he needs support for both:

  • ft^(3/5)
  • m^1.5

The reason we need fraction parts is to preserve precision for as long as possible so we don't run into a case like m^1.0/m^0.9999... not cancelling right.

Can you think of any reason I wouldn't need to worry about that and could instead just convert all fractional exponents into Doubles and still get the right behavior during dimensional analysis?

The main use case is for intermediate units that when combined reduce into units that make sense.
So an equation might be composed of units that have no real world analogue but when combined cancel out with each other appropriately to create a "real" unit.

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

Successfully merging this pull request may close these issues.

2 participants