Jump to content

Wikifunctions:Type proposals/Rational number

From Wikifunctions

Done: Z19677

Summary

A rational number is a number that can be expressed as a fraction with both numerator and denominator being natural numbers and with the denominator not being equal to zero. A rational number also has a sign.

Uses

A Rational number is the most precise representation of the result of division. It can be displayed with an arbitrarily large number of decimal places, or in bases other than base 10. Equally, it can be displayed as a fraction or an integer and a remainder. Such transformations may lead to a loss of precision, but the value represented by the object is not changed by them.

See en:Rational_data_type#Language_support for programming languages that support Rational numbers as a primitive data type or extension.

Structure

A rational number is an object with three keys, one for the sign, one containing the natural number value of the numerator and the other containing the natural number value of the denominator.

Example values

Value 0

{
  "type": "rational number",
  "sign": "neutral",
  "numerator": {
    "type": "natural number",
    "value": "0"
  },
  "denominator": {
    "type": "natural number",
    "value": "1"
  }
}
{
  "Z1K1": "Zmmmmm",
  "ZmmmmmK1": "Z16661",
  "ZmmmmmK2": {
    "Z1K1": "Z13518",
    "Z13518K1": "0"
  },
  "ZmmmmmK3": {
    "Z1K1": "Z13518",
    "Z13518K1": "1"
  }
}

Value 1/3

{
  "type": "rational number",
  "sign": "positive",
  "numerator": {
    "type": "natural number",
    "value": "1"
  },
  "denominator": {
    "type": "natural number",
    "value": "3"
  }
}
{
  "Z1K1": "Zmmmmm",
  "ZmmmmmK1": "Z16660",
  "ZmmmmmK2": {
    "Z1K1": "Z13518",
    "Z13518K1": "1"
  },
  "ZmmmmmK3": {
    "Z1K1": "Z13518",
    "Z13518K1": "3"
  }
}

Value 8

{
  "type": "rational number",
  "sign": "positive",
  "numerator": {
    "type": "natural number",
    "value": "8"
  },
  "denominator": {
    "type": "natural number",
    "value": "1"
  }
}
{
  "Z1K1": "Zmmmmm",
  "ZmmmmmK1": "Z16660",
  "ZmmmmmK2": {
    "Z1K1": "Z13518",
    "Z13518K1": "8"
  },
  "ZmmmmmK3": {
    "Z1K1": "Z13518",
    "Z13518K1": "1"
  }
}

Value -3/5

{
  "type": "rational number",
  "sign": "negative",
  "numerator": {
    "type": "natural number",
    "value": "3"
  },
  "denominator": {
    "type": "natural number",
    "value": "5"
  }
}
{
  "Z1K1": "Zmmmmm",
  "ZmmmmmK1": "Z166612",
  "ZmmmmmK2": {
    "Z1K1": "Z13518",
    "Z13518K1": "3"
  },
  "ZmmmmmK3": {
    "Z1K1": "Z13518",
    "Z13518K1": "5"
  }
}

Persistent objects

Possibly approximations for irrational numbers like π and Euler’s number (e), but maybe none.

Validator

The validator ensures that:

  • The sign is neutral iff the numerator is 0
  • The denominator is one or greater
  • The fraction is in simplified form

Identity

Two rational numbers are the same if the sign, numerator and denominator are the same on both objects. In case one of the values is not simplified, the values are simplified first and then compared.

Converting to code

Python

The object will be converted to the standard library type fractions.Fraction.

JavaScript

The object will be converted into an object with two keys, with the sign of the numerator corresponding to the sign of the rational number:

{
    K1: -3n,
    K2: 5n
}

When converting from JavaScript to a Rational number object, the Sign is inferred from the product of the numerator and the denominator, and both integers are divided by their greatest common divisor.

[As the JavaScript object is unsimplified, shouldn’t we allow for the denominator being negative?]--GrounderUK (talk) 23:33, 13 November 2024 (UTC)[reply]

Done with this change. --DVrandecic (WMF) (talk) 08:57, 14 November 2024 (UTC)[reply]
Section text revised to reflect this change.--GrounderUK (talk) 09:22, 14 November 2024 (UTC)[reply]

Renderer

Renderers are the responsibility of the community.

They could output "numerator/denominator", e.g. "-3/5".

Parsers

Parsers are the responsibility of the community.

They should be able to read the same form the renderer renders out as. ("numerator/denominator"). They should also be able to read decimal inputs, and automatically transform it into a rational number. Parsers should automatically simplify.

Alternatives

Wikifunctions:Type_proposals/float64 is a possible alternative, although less precise for some situations.

Instead of a separate sign, there could be an integer as the numerator. Because an integer is already structured as Sign plus Natural number, a Rational number ends up as Sign + Natural number + Natural number either way.

Instead of allowing integers only in the numerator, they could also be used for the denominator. (This structure would also have an additional explicit Sign, but that would be wholly redundant).

Should we allow a denominator of 0 to represent +Inf and -Inf? It would also avoid having to return a special error when trying to divide by 0.

Please don’t. Infinities are not rational numbers and a fraction with a denominator of zero is not an infinity (or a rational number). Divide by zero is a special case, even in IEEE 754 (although the result is additionally represented as an infinity). Personally, I’m happy with an undefined value being an error (which is what any calculator would do) but it would be convenient to know where the exception occurred.--GrounderUK (talk) 15:47, 8 November 2024 (UTC)[reply]

Comments

  • Support Support as I believe this would be a good idea - irrationals are quite rare in most situations. infernostars (talk) (contribs) 05:46, 21 July 2024 (UTC)[reply]
  • Support Support I have a slight preference for a signed ratio of Natural numbers. I don’t believe that we need to restrict the Type to reduced fractions (as implied by the validation). We should make reduction an explicit function. In some contexts, such as currency and measurements, “simplifying” the fraction adds complexity (a fraction of a dollar would generally be better left as a number of hundredths, for example).--GrounderUK (talk) 10:48, 21 July 2024 (UTC)[reply]
    Identity would not necessarily imply reduction. 16/100 is not the same as 4/25; it is just precisely equal in value. GrounderUK (talk) 10:55, 21 July 2024 (UTC)[reply]
    According to the python documentation, fractions.Fraction does not support unsimplified fractions and will simplify them. -- ScienceD90 (talk) 12:19, 21 July 2024 (UTC)[reply]
    Maybe the uses I envisage would be better supported by a mixed number type anyway. Conceptually, that would be natural number and proper fraction with an explicit Sign (which applies to the implicit sum of the two parts). This could still be converted to a Python fraction but then converting the fractional part back would be an interesting challenge for such a type (if it’s not necessarily in its simplest form). I’m happy to consider this off-topic here, but it does increase my preference for a separate Sign with Natural numbers here, since that would be more consistent. GrounderUK (talk) 13:33, 21 July 2024 (UTC)[reply]
    I don't quite understand what you mean by "signed ratio of Natural numbers". I read https://www.themathpage.com/aReal/ratio-natural-numbers.htm. Would you be willing to give an example of how this would look/be stored? Is there a proposal for "signed ratio of Natural numbers" yet? So9q (talk) 11:21, 3 August 2024 (UTC)[reply]
    A Sign and two Natural numbers, the numerator and the denominator in a positive (or zero) fraction. As far as I can see, this Type proposal should be the type proposal for exactly that, although it’s N0 / N1 (signed) rather than Z / N1, as drafted. For example, “minus two thirds” would be represented as –(2/3) rather than (–2)/3. GrounderUK (talk) 09:37, 6 August 2024 (UTC)[reply]
    I think this is a good idea, however, if we do this, I think we should keep the Javascript conversions the same as a pair of numbers would be easier to work with. -- ScienceD90 (talk) 11:45, 6 August 2024 (UTC)[reply]
    I'm still not sure I understand what you mean. Do you mean you want the sign out of the numerator like this?
{
  "type": "rational number",
  "sign": "negative",
  "numerator": {
    "type": "integer",
    "absolute value": {
      "type": "natural number",
      "value": "3"
    }
  }
  "denominator": {
    "type": "natural number",
    "value": "5"
  }
}
{
  "Z1K1": "Zmmmmm",
  "Z16683K1": "Z166612",
  "ZmmmmmK1": {
    "Z1K1": "Z16683",
    "Z16683K2": {
      "Z1K1": "Z13518",
      "Z13518K1": "3"
    }
  }
  "ZmmmmmK2": {
    "Z1K1": "Z13518",
    "Z13518K1": "5"
  }
}

So9q (talk) 14:41, 6 August 2024 (UTC)[reply]

Yes, but not like that. There would be three keys: sign (ZmmmmmK1), numerator (ZmmmmmK2) and denominator (ZmmmmmK3). Like denominator, numerator would be a Natural number, rather than an Integer.--GrounderUK (talk) 16:47, 7 August 2024 (UTC)[reply]
  • Support Support I have a fairly strong preference for a signed ratio of Natural numbers. --99of9 (talk) 12:08, 21 July 2024 (UTC)[reply]
    I would think this is easier for JavaScript, as it'd be just two keys. I suppose we could do the multiplication by sign in conversion, but in that case, what's the point of not just using an Integer in the first place? infernostars (talk) (contribs) 19:18, 22 July 2024 (UTC)[reply]
    Two reasons for me:
    1. Since this is the strictly accurate mathematical Type for rationals, it would be nice to be in concordance with the mathematical fact that the sign is no more associated with the numerator than it is with the denominator. Think of the calculation (3-1)/(0-2), which needs to return [-,1,1] or equivalent. To me it makes sense to pull the sign out the front from wherever it is, rather than positivising the denominator.
    2. Many compositions will branch according to the sign, and it would be easier to directly read it from the key rather than dig it out of the integer.
    99of9 (talk) 23:53, 6 November 2024 (UTC)[reply]
  • Support Support as floats are imprecise and these are more precise for many situations -- ScienceD90 (talk) 12:11, 21 July 2024 (UTC)[reply]
  • Support Support on a lower priority than floats. Feeglgeef (talk) 01:16, 7 November 2024 (UTC)[reply]

Specific implementation details comments

Thanks everyone for the discussion! To make it clear, implementing this does not mean, we won't implement float64 -- it's not an either/or! Unless any unforeseen happens, we plan to implement this next week. There are two questions that I need to have resolved before, though:

  1. Do we have a) three keys, one with an integer and the other with a natural number, or b) three keys, one of sign and the other two natural numbers?
  2. Do all values have to be a) simplified, or b) not?
  3. If unsimplified values are allowed, does a) identity hold for, e.g. 1/2 and 2/4, or are b) these different values?

I have a preference for 1a. Regarding 2, I think it should be 2b, or else we can't have functions checking "is this simplified" etc. If we do 2b we probably should say that 1/2 and 2/4 are not identical, but equal, i.e. 3b, or else things get very confusing. On the other hand, if we want to use Python fractions, we must choose 2a. So if we choose 2b, we cannot use Python fractions for representing the value in Python. (We can still use the fractions library when coding).

Without convincing arguments otherwise, or with many people telling me otherwise, that's how I would implement it. What do you think? --DVrandecic (WMF) (talk) 16:30, 7 November 2024 (UTC)[reply]

@GrounderUK, Infernostars, Arlo Barnes, ScienceD90, 99of9, and So9q: In the hope you see this in time :) --DVrandecic (WMF) (talk) 18:02, 7 November 2024 (UTC)[reply]

2a

My opinion is a for 1 and 2, and if b is chosen for 2, I would prefer them to maintain identity. Specifically I would support the type converters doing this for us. This would allow more simplicity and just be generally better and easier. I don't see a real need for a "is this simplified" function, as this doesn't seem to be the intended use for this type. Feeglgeef (talk) 16:47, 7 November 2024 (UTC)[reply]
@Feeglgeef -- You didn't actually state your choices :) --DVrandecic (WMF) (talk) 18:02, 7 November 2024 (UTC)[reply]
I... did? "a for 1 and 2" Feeglgeef (talk) 18:03, 7 November 2024 (UTC)[reply]
ah, I misread, apologies! I thought the "a" was the indefinite article, not option a! That's a weird misread by myself, thanks for explaining! --DVrandecic (WMF) (talk) 18:24, 7 November 2024 (UTC)[reply]
Alright. I think everyone is agreement for 1a (that I've talked to). The only things really disputed are 2a/2b. Feeglgeef (talk) 18:32, 7 November 2024 (UTC)[reply]

2b

  1. Either is fine.
  2. I was thinking (b) unsimplified (because simplification is a lossy conversion).
  3. Yes different representations of the same numerical value are equal but not identical (“same”) which also applies to integers and natural numbers (relative to one another and to rationals). We should probably rename Z13522 to “same Natural number”. Z13052 will give numerical equality and Z18683 will give numerical equality within rationals (although I expect we would have a specific equality function as well).
GrounderUK (talk) 18:51, 7 November 2024 (UTC)[reply]
I agree with all this :) (same=identical)!=equal So9q (talk) 11:28, 18 November 2024 (UTC)[reply]

2c

1a please. Is there an in-between option for 2? I agree that it would be nice to accept any ratio, but it would also be very nice to use the Python fraction. I wouldn't want to have to pass arguments into a fraction type/library every time it got into Python code. So, option 2c, is there anything wrong with *accepting* any ratio (i.e. not building gcd into the validator), but almost always *returning* the canonical version (especially when returning from Python code). This way we could still have a function "Is this simplified?", but it would need to be a composition rather than a Python implementation. We already have some functions that can't be implemented in code, and others that can't be implemented as compositions. 99of9 (talk) 23:18, 7 November 2024 (UTC)[reply]
I've been asked to mention JS. JS would receive the unsimplified natural numbers, and hence could do the simplification test. The returns from JS could be chosen on a function level whether they want to just return unsimplified or not. I expect they usually would just return unsimplified, because the gcd simplification would be annoying to put into every function. Then when we test the results of fraction functions, we would usually test using is_equivalent_fraction rather than is_identical_fraction. 99of9 (talk) 23:30, 7 November 2024 (UTC)[reply]
Yeah, but then the user wouldn’t know whether the result is simplified. It’s a tough call but the Python user is also likely to want to use decimals… It’s almost as if the conversion option is a variable (which is sub-typing at the function level and, maybe, “generic”). Can we go with true rationals this week and defer variants? GrounderUK (talk) 23:53, 7 November 2024 (UTC)[reply]
"true rationals" are not pairs of integers, they are equivalence classes of pairs of integers. As I understand it, for mathematicians 2/4 *is* the same rational number as 1/2. --99of9 (talk) 00:20, 8 November 2024 (UTC)[reply]
Fair point. 2/4 and 1/2 are different representations of the same rational number (value) in my mind too. Said mind is conflicted, however, as I still favour 2b (whilst not objecting to 2a). Other options are worth exploring in the future but should not delay delivery of 2a or 2b in the coming week. GrounderUK (talk) 02:25, 8 November 2024 (UTC)[reply]

2d

Actually, option 2d, if we wanted JS to behave the same as Python, simplification could be done in the JS conversion from code. Then it would only be written once and the function writer would never need to do it, but would also never be able to choose to return unsimplified. 99of9 (talk) 23:37, 7 November 2024 (UTC)[reply]

I think this is better for consistency. I'd like lines to be drawn at programming languages vs compositions. To summarize it with an idiom, JS vs Python is like comparing apples to oranges, but comparing JS to compositions is like comparing apples to elephants. Feeglgeef (talk) 23:51, 7 November 2024 (UTC)[reply]
I agree with this; rational numbers implies [for my 2 cents, as a relative outsider to this] that it would be simplified always. I also agree with 3b though if we go through with that, and I have no preference for option one. infernostars (talk) (contribs) 21:35, 10 November 2024 (UTC)[reply]

Implemented as Beta

It has been implemented as Z19677, going with the three-key solution, and using Python Fractions, which means it should always be simplified.

I am still wondering if the simplification is a good idea, the implementation for addition looks pretty complicated because of this, and I am worried whether we will find good patterns for compositions to deal with this, but here we are now!

I would suggest to follow Postel's law: be liberal in the values you accept, be strict in the values you return. --DVrandecic (WMF) (talk) 12:01, 13 November 2024 (UTC)[reply]

JavaScript addition is much tidier now the sign is in the numerator and simplifying the JavaScript result in conversion from code keeps things simple for coders and consistent with Python. I think we ended up with a very reasonable compromise, thank you! GrounderUK (talk) 00:14, 14 November 2024 (UTC)[reply]
I agree. Thanks for consulting us @DVrandecic (WMF) <3 So9q (talk) 11:32, 18 November 2024 (UTC)[reply]