12

I've figured out how to display the repeating part of a repeating decimal using OverBar.

repeatingDecimal doesn't actually work as a repeating decimal. I'd like to make a variation of it that looks and behaves like a repeating decimal.


Question

How could I make a working repeating decimal representation (possibly using Interpretation[])?


Background

Please excuse me if I ramble. This is my first question and I wanted to be clear about what I have in mind.

The following will "draw" a repeating decimal.

repeatingDecimal[q2_] :=
 Module[{a},
  a[{{nr__Integer}, pt_}] := 
   StringJoin[
    Map[ToString, 
     If[pt > -1, Insert[{nr}, ".", pt + 1], 
      Join[{"."}, Table["0", {Abs[pt]}], {nr}]]]];
  (* repeating only *)

  a[{{{r__Integer}}, pt_}] := 
   Row[{".", OverBar@StringJoin[Map[ToString, {r}]]}];

  (* One or more non-repeating; 
  more than one repeating digit KEEP IN THIS ORDER!! *)
  a[{{nr__, {r__}}, pt_}] := 
   Row[{StringJoin[
      Map[ToString, 
       If[pt > -1, Insert[{nr}, ".", pt + 1], 
        Join[{"."}, Table["0", {Abs[pt]}], {nr}]]]], 
     OverBar@StringJoin[Map[ToString, {r}]]}];
  (* One or more non-repeating; one repeating digit *)

  a[{{nr__, r_Integer}, pt_}] := 
   Row[{StringJoin[Map[ToString, {nr}]], ".", 
     OverBar@StringJoin[Map[ToString, r]]}];
  a[RealDigits[q2]]]

So

repeatingDecimal[7/31]

displays a repeating decimal properly (shown here as a picture so that the OverBar appears).

Repeating decimal displayed

Looking under the hood, it's really just an imposter, an image of a repeating decimal ...

In[]:= repeatingDecimal[7/31]//FullForm
Out[]:= Row[List[".",OverBar["225806451612903"]]]

Of course, it doesn't behave like a number:

% + 24/31

fraction plus repeating decimal

I'd like the addition to yield: 1


Edit: A cleaned up version of repeatingDecimal

Leonid showed how to wrap Format around the routine and to supply up-values for adding and multiplying repeated decimals. Very helpful! It will take some time for me to be comfortable with up and down values.

What follows below is essentially the streamlined version of the code suggested by Mr.Wizard. I set the OverBar above each repeating digit to allow line-breaking. (A single OverBar above Row looks tidier but cannot break when the right screen-margin is reached.)

ClearAll[repeatingDecimal]

repeatingDecimal[n_Integer | n_Real] := n

Format[repeatingDecimal[q_Rational]] := Row @ Flatten[
   {IntegerPart@q, ".", RealDigits@FractionalPart@q} /.
    {{nr___Integer, r_List: {}}, pt_} :> {Table[0, {-pt}], nr, OverBar /@ r}
  ]

repeatingDecimal[q_] + x_ ^:= q + x
repeatingDecimal[q_] * x_ ^:= q * x
repeatingDecimal[q_] ^ x_ ^:= q ^ x

The table below shows some output from repeatingDecimal:

n1 = 1; n2 = 15; ClearAll[i, k, r];
TableForm[Table[repeatingDecimal[i/j], {i, n1, n2}, {j, n1, n2}], 
TableHeadings -> {None, Table[("r")/k, {k, n1, n2}]}]

enter image description here


Checking the solution: Operating with repeating decimals

Let's now check the addition and multiplication of repeating decimals:

a = repeatingDecimal[7/31];
b = repeatingDecimal[24/31];
Print["a = ", a]
Print["b = ", b]
Print["a + b = ", a, " + ", b, " = ", a + b]
Print["7/31 \[Times] 24/31 = " , (7/31)* (24/31)]
Print["a\[Times]b = ", a*b, " = \n", repeatingDecimal[a*b]]
Print[N[168/961, 465]]

repeating decimal addition and multiplication

So addition and multiplication of repeating decimals work as desired. Power also appears to work properly.

Notice that 168/961 occupies 465 places to the right of the decimal point. After that, it starts to repeat. The results match those of N[168/961, 465], except for the OverBar, although line-breaks occur at different places. And, as is to be expected, this jibes with the following:

digits = RealDigits[168/961]
Length[digits[[1, 1]]]

465 digits


Some effects of the Format[] wrapper on the behavior of N[] in summing repeated decimals

Mr.Wizard suggested that the Format wrapper is superfluous for the cases of Integers and Reals.

Let's consider how the following two additions

repeatingDecimal[7/31] + repeatingDecimal[24/31]
N@repeatingDecimal[7/31] + N@repeatingDecimal[24/31]

behave in four different cases:

Case 1: Results when Format wrapped around repeatingDecimals for Reals and Integers and up values are ON

Case 1

As expected, the first addition yields an integer, the second a decimal.


Case 2: Results when Format NOT wrapped around repeatingDecimals for Reals and Integers but up values are ON

Case 2

The Format wrapper around Reals and Integers doesn't affect the additions at hand.


Case 3: Results when Format wrapped around repeatingDecimals for Reals and Integers but up values are OFF

Case 3

If upvalues are OFF, Format prevents addition from happening.


Case 4: Results when Format NOT wrapped around repeatingDecimals for Reals and Integers and up values are OFF

Case 4

If upvalues are OFF and Format` NOT wrapped around repeatingDecimals for Reals and Integers , the second addition works as expected.

All the more reason to remove the Format wrapper for the case of reals and integers.


Anyone have any remarks about the different outcomes in Cases 3 and 4?

DavidC
  • 3,036
  • 1
  • 17
  • 30
  • @Leonid Shifrin Your suggestions for adding and multiplying repeating decimals via up-values worked like a charm. In the section, **Checking the Solution** I illustrated the operations. – DavidC Mar 07 '11 at 03:02
  • @David Carraher do you actually want the Blue/Red/Gray highlighting, or is that just for development? – Mr.Wizard Mar 07 '11 at 13:18
  • @Mr.Wizard The coloring was just for development. When debugging, I found it useful see what patterns were kicking in. Your feedback was useful for me to realize that the original code wasn't air tight. – DavidC Mar 07 '11 at 17:55
  • I removed the coloring in the "final" version. – DavidC Mar 07 '11 at 19:50
  • If you allow `OverBar` to be applied character-wise, another simplification can be made: removal of the `If` statement. I had considered and rejected this because it deviated from your original styling. I notice your post has become a "community wiki" so I will edit it accordingly. – Mr.Wizard Mar 08 '11 at 10:35
  • @Mr Wizard Yesterday I applied `OverBar` to each character to let `Row` make line-breaks. So feel free to streamline the code even more. – DavidC Mar 08 '11 at 10:49
  • I have a less obfuscated version of the Plus/Times handler that takes advantage of the Orderless property. I'll make the edit, and if you don't like it (or it's broken), roll it back. Also, you do not need the `;` after `ClearAll[...]` or `lhs := rhs` because neither prints anything. – Mr.Wizard Mar 08 '11 at 11:19
  • BTW, if you do not want the small gaps between the overbars, I can `Riffle` a `\[NegativeThinSpace]` -- do you want that? – Mr.Wizard Mar 08 '11 at 11:33
  • @Mr. Wizard Thanks. The Plus/Times handler does seem a bit more readable now. I'd skip the `\[NegativeThinSpace]` insertions, as clever as the idea is, so as to favor lean code over presentation aesthetics. Actually, I'm unsure whether distinct `OverBar`s are noticeably more pleasing. – DavidC Mar 08 '11 at 14:03
  • I'm a little puzzled why `N[repeatingDecimal[7/31]]` works even without being taught. – DavidC Mar 08 '11 at 16:02
  • @David I just noticed your question about `N`. This happens because the internal (not print) form of `repeatingDecimal[7/31]` is still `repeatingDecimal[7/31]` and `N` goes inside expressions, so this becomes `repeatingDecimal[0.2258]`. This also makes me wonder if you actually want the wrapper on Real and Integer arguments. For example **without** the `UpSet` rules, try: `N@repeatingDecimal[7/31] + N@repeatingDecimal[24/31]` and see that the wrappers are still there, so they do not add. However, if you remove `Format` from `Format[repeatingDecimal[n_Integer | n_Real]] := n`, they will. – Mr.Wizard Mar 10 '11 at 17:32
  • @Mr.Wizard Tests on the four conditions (`Format` wrapped or NOT around reals and integers) and upvalues (on and off) confirm your observation that the `Format` wrapper around the reals and integers is unnecessary. – DavidC Mar 11 '11 at 16:25
  • @Mr.Wizard Some additional remarks about Format wrappers have been added to the Wiki community question. – DavidC Mar 11 '11 at 17:10
  • Some really neato answers over here: http://mathematica.stackexchange.com/questions/15818/can-mathematica-show-me-a-fraction-with-a-repeating-decimal-notation/15827#comment46944_15827 – Warren P Dec 07 '12 at 19:05

2 Answers2

10

You shouldn't have given your repeatingDecimal DownVaues, but rather, FormatValues:

ClearAll[repeatingDecimal];
Format[repeatingDecimal[q2_]] := 
Module[{a}, 
 a[{{nr__Integer}, pt_}] := 
 StringJoin[
  Map[ToString, 
   If[pt > -1, Insert[{nr}, ".", pt + 1], 
  Join[{"."}, Table["0", {Abs[pt]}], {nr}]]]];
  (*repeating only*)
 a[{{{r__Integer}}, pt_}] := 
 Row[{".", OverBar@StringJoin[Map[ToString, {r}]]}];
(*One or more non-repeating;
more than one repeating digit KEEP IN THIS ORDER!!*)
a[{{nr__, {r__}}, pt_}] := 
 Row[{StringJoin[
   Map[ToString, 
    If[pt > -1, Insert[{nr}, ".", pt + 1], 
     Join[{"."}, Table["0", {Abs[pt]}], {nr}]]]], 
  OverBar@StringJoin[Map[ToString, {r}]]}];
(*One or more non-repeating;one repeating digit*)
a[{{nr__, r_Integer}, pt_}] := 
  Row[{StringJoin[Map[ToString, {nr}]], ".", 
   OverBar@StringJoin[Map[ToString, r]]}];
a[RealDigits[q2]]]

Then, you can give it also UpValues, to integrate with common functions, for example:

repeatingDecimal /: Plus[left___, repeatingDecimal[q_], right___] := left + q + right;
repeatingDecimal /: Times[left___, repeatingDecimal[q_], right___] :=  left * q * right;

Then, for example,

In[146]:= repeatingDecimal[7/31]+24/31

Out[146]= 1

You can extend this approach to other common functions which you may want to work with repeatingDecimal.

Leonid Shifrin
  • 22,129
  • 4
  • 66
  • 98
  • Thanks for the tips. The distinction between Down and UpValues has never been clear to me. I'll see what I can make of it now in this context. – DavidC Mar 05 '11 at 00:48
  • 1
    In what case is the last pattern `a[{{nr__, r_Integer}, pt_}]` matched? – Mr.Wizard Mar 05 '11 at 11:40
  • Addition of a repeatingDecimal and a fraction returns a fraction, as you showed. Addition of an 'exact decimal' (repeatingDecimal) and a regular decimal defaults yields a "real" number with floating point (im)precision. Perhaps that's not too surprising after all. – DavidC Mar 05 '11 at 13:29
  • @David This seems logical, but you could have introduced more complex rules to handle such cases differently, say by rationalizing other numbers to certain precision. This would IMO be however more ambiguous overall. – Leonid Shifrin Mar 05 '11 at 13:44
  • Yes. As long as there is a `repeatingDecimal[]` wrapper around a decimal, addition will work, returning a common fraction. If I wrap `repeatingDecimal` around that, it should yield a (repeating) decimal. – DavidC Mar 05 '11 at 14:04
2

Here is a possible refactoring of your updated code. I think it works this time (fingers crossed). If you do not need the color highlighting, you can leave off ~Style~ and the rest of that line.

ClearAll[repeatingDecimal];

Format[repeatingDecimal[n_Integer | n_Real]] := n;

Format[repeatingDecimal[q_Rational]] :=
 Row[{IntegerPart@q, ".", RealDigits@FractionalPart@q}] /.
  {{ nr___Integer, r_List:{} }, pt_} :>
   Row@Join[
      "0" ~Table~ {-pt},
      {nr},
      If[r === {}, {}, {OverBar@Row@r}]
      ] ~Style~ If[r === {}, Blue, If[{nr} === {}, Red, Gray]]

repeatingDecimal /:
  (h : Plus | Times)[left___, repeatingDecimal[q_], right___] :=
    h[left, q, right];

I will leave this older version here for reference, but I am now making edits to the Question community wiki.

Mr.Wizard
  • 23,689
  • 5
  • 41
  • 116
  • Mr.Wizard, Thanks, you definitely found a bug. However, your code is not exactly equivalent to my original code. It runs into problems for fractions with a denominator of 10. – DavidC Mar 05 '11 at 13:49
  • @Mr.Wizard Not exactly with a "denominator of 10", but here are some problematic cases for a "numerator" of 10. Try `repeatingDecimal[10/3]`, `repeatingDecimal[10/7]` and `repeatingDecimal[10/9]`. I'll look carefully at what happens, working forward from `RealDigits`. For each of these cases, as well as for `62920/3` that you noted earlier, Mma complains: `Insert called with 4 arguments; 3 arguments are expected. ` – DavidC Mar 05 '11 at 16:49
  • @David Carrahar The error message you mention is a secondary one. It is caused by the earlier failure of Insert to insert anything due to the list being empty. It returns unevaluated and then Append adds a fourth argument to Insert's argument list which then yields the abovementioned message. Main problem here is RealDigits' output that takes a number of different formats depending on the input and which arfer not matched adequately. – Sjoerd C. de Vries Mar 05 '11 at 19:41
  • @Sjoerd I agree that the trouble seems to have been in the variety of structures that `RealDigits` returns, depending on the combination of non-repeating and repeating digits. I straightened out most of the issues in Edit 2, but I wouldn't be surprised if one or two cases still got away. – DavidC Mar 05 '11 at 22:22
  • 1
    @David Carraher please see this new version. I deleted my old comments because they did not apply any longer. – Mr.Wizard Mar 07 '11 at 13:38