Message Boards Message Boards

Output repeating decimal numbers with an overline?

Posted 7 years ago

I would like to output repeating decimal numbers with an overline over the repeating digits. For instance, 13/7 becomes 1.857142, but with a line over the bolded digits instead of bold. I've been trying to write such a function myself using RealDigits, but so far I'm making slow progress. I know it can be done because Wolfram Alpha has such an output. Has anyone tackled this problem yet? It would save me a bunch of time if you'd share your solutions. I'm using the Wolfram Development Platform in the Desktop environment, if that makes any difference.

Thanks in advance,

Mark Greenberg

POSTED BY: Mark Greenberg
12 Replies

I am glad that you found my code instructive. I regret my laziness in leaving the uninformative letter c_ when I could have written the more revealing positionOfDecimalPoint_. It took me some time to figure out the output of RealDigits when applied to rational numbers. The documentation on that has room for improvement.

POSTED BY: Gianluca Gorni
Posted 7 years ago

When a Wolfram expert provides a solution to one of my questions, it is often an elegant bit of programming that makes use of advanced features of the Wolfram Language... at least advanced from my relative novice perspective. I pull apart such a solution in an effort to learn from it. I'll deconstruct this repeating decimal solution here not only so that I will learn new techniques, but so that other novices may benefit as well.

I asked how a number could be output as a repeating decimal with an line over the repeating part. The solution (by Gianluca Gorni with a slight tweak by Sander Huisman) was this:

normalizeDigitSequence =
  {{{beforeRecurring___, {recurring__}}, c_?NonPositive} :>
    {{0, beforeRecurring, {recurring}}, c + 1},
   {{beforeRecurring___, {recurring__}}, c_?Positive} /; 
     Length[{beforeRecurring}] < c :>
    {{beforeRecurring, First[{recurring}],
      RotateLeft[{recurring}]}, c}};
addOverlineToRepeating =
  {{beforeRecurring___, {recurring___}}, c_?Positive} /; 
    Length[{beforeRecurring}] >= c :>
   Row[Append[Insert[{beforeRecurring}, ".", c + 1],
     OverBar[Row[{recurring}]]]];
RealDigits[13/7] //. normalizeDigitSequence /. addOverlineToRepeating

Here is an explanation of this gem:

First, the input into this whole thing is a number wrapped in RealDigits[], which outputs a list of two items: {list of digits, a decimal offset}. RealDigits[2] outputs {{2}, 1}, and RealDigits[.2] outputs {{2}, 0}. The list of digits can also contain a list of repeating digits, so RealDigits[8/3] outputs {{2, {6}}, 1}, and RealDigits[13/7] outputs {{1, {8, 5, 7, 1, 4, 2}}, 1}.

The solution has three parts.

normalizeDigitSequence turns the RealDigits list into a list suitable for the output number.

This is accomplished by assigning a delayed replacement rule, :>, to the variable normalizeDigitSequence. A delayed replacement rule happens whenever the left side is used later in code.

{{beforeRecurring___, {recurring__}}, c_} :>
    {{0, beforeRecurring, {recurring}}, c + 1}

This means put a 0 at the front of the list of non-repeating digits and add 1 to the decimal point offset. But that should only happen when c is less than 1, so c_ is written as c_?NonPositive.

A second delayed replacement rule takes care situations where c is positive:

{{beforeRecurring___, {recurring__}}, c_?Positive} :>
    {{beforeRecurring, First[{recurring}], RotateLeft[{recurring}]}, c}

This means copy the first digit from the list of repeating digits to the end of the list of non-repeating digits and then rotate the order of the list of repeating digits so that {1, 2, 3} becomes {2, 3, 1}. But this should only happen when length of the list of non-repeating digits is less than c, the decimal offset. That's why there is a condition on this rule:

/; Length[{beforeRecurring}] < c

addOverlineToRepeating is the second variable assigned to a delayed replacement rule. The rule only applies when c is positive and also has a conditional that restricts the replacement to cases when the length of the non-repeating digit list is greater than or equal to c:

{{beforeRecurring___, {recurring___}}, c_?Positive} /; 
    Length[{beforeRecurring}] >= c :>
   Row[Append[Insert[{beforeRecurring}, ".", c + 1],
     OverBar[Row[{recurring}]]]]

This says make a list of the non-repeating digits, put a decimal point at location c in this list, put an overbar on the list of repeating digits, and then join the two lists. Row[] turns this list of items (digits, a decimal, and overlined digits) into a continuous number.

RealDigits[13/7] //. normalizeDigitSequence /. addOverlineToRepeating

This applies all the replacement rules to some number. But there is something subtle going on here, too. Notice that the first replacement is //. instead of /.? The first one means replace repeatedly. It will keep applying the replacement rules over and over until the output stops changing. That is important here because there may be more than one digit that needs to be copied from the repeating digit list to the non-repeating list, or more than one zero that needs to precede the repeating decimals.

In the end, we have a perfect solution to the problem (as far as my needs are concerned, anyway) that is both elegant and compact.

Here are a few things I learned from this solution:

  • a = b :> c is a way to save replacement rules for later use, very handy.
  • a_?Positive is a way to restrict a pattern, “Positive” possibly being any conditional
  • a_ /; b > c :> d is a way to selectively apply a replacement rule
  • Row[{1, 2, 3}] is a way to reconstitute a number (for display purposes)
  • a //. _ ? b is a way to apply the same replacement over and over

Wow! I love how depth and complexity comes from such simple Wolfram syntax!

I also learned that I should check Stack Overflow and Stack Exchange before asking a question in the Wolfram Community. If I had, though, I wouldn't have seen Gianluca's marvelous solution. But in the future I will check SO and SE first.

I hope this helps someone. It helped me. : )

Mark

POSTED BY: Mark Greenberg

The recurring digit sequence seems complete to me. From RealDigits[130/7] you get {{1, {8, 5, 7, 1, 4, 2}}, 2}, which is equivalent to {{1, 8, {5, 7, 1, 4, 2, 8}}, 2}. The sequence lends its first digit to the pre-recurring sequence and gets it back at the end.

As for the underscore, yes, now that I think of it it is indeed unexpected that Overscript[3, Blank[]] should work. I really meant to write underscore, not Blank[].

POSTED BY: Gianluca Gorni
Posted 7 years ago

The sequence lends its first digit to the pre-recurring sequence and gets it back at the end.

Sorry, I hadn't noticed that. I take my words back.

POSTED BY: Alexey Popkov

What is wrong with 130/7? I simply arranged for the ovlerline non to cross over the decimal point. I was not aware of OverBar, which is indeed shorter. However, Overscript is regularly documented. Curiously,

OverBar[x] === Overscript[x, _]

gives False, but if you evaluate

{OverBar[x], Overscript[x, _]}

and look at the underlying BoxData in the output cell, you will see that they are represented identically as OverscriptBox["x", "_"]. If you copy and paste one of them into an expression that expects the other, you may run into mysterious trouble.

POSTED BY: Gianluca Gorni

I know Overscript is documented, not that the second character can be a blank (not a string of an underscore)! Or did I miss something?

POSTED BY: Sander Huisman
Posted 7 years ago

What is wrong with 130/7? I simply arranged for the ovlerline non to cross over the decimal point.

That's the problem: for 130/7 the recurring digit sequence is incomplete, hence the output is wrong.

POSTED BY: Alexey Popkov
Posted 7 years ago

The output from RealDigits is complicated to use. Here is what I came up with:

normalizeDigitSequence =
  {{{beforeRecurring___, {recurring__}}, c_?NonPositive} :>
    {{0, beforeRecurring, {recurring}}, c + 1},
   {{beforeRecurring___, {recurring__}}, c_?Positive} /; 
     Length[{beforeRecurring}] < c :>
    {{beforeRecurring, First[{recurring}],
      RotateLeft[{recurring}]}, c}};
addOverlineToRepeating =
  {{beforeRecurring___, {recurring___}}, c_?Positive} /; 
    Length[{beforeRecurring}] >= c :>
   Row[Append[Insert[{beforeRecurring}, ".", c + 1],
     Overscript[Row[{recurring}], _]]];
RealDigits[13/7] //. normalizeDigitSequence /. addOverlineToRepeating

This is meant for display only. It does not evaluate to a number.

POSTED BY: Gianluca Gorni
Posted 7 years ago

Thank you, Gianluca. It works perfectly!

I love getting solutions from the community. Usually they employ techniques that I can learn from, as does this one. I'll have to pull it apart to see how it works. Perhaps I I'll write a step-by-step explanation then, for the benefit of others like me who want to learn more about the mysteries of the Wolfram language.

Thanks again,

Mark

POSTED BY: Mark Greenberg

Instead of using the undocumented (?) Overscript with a Blank as a second argument, one can also use OverBar.

POSTED BY: Sander Huisman
Posted 7 years ago

This code gives wrong result, for example, for 130/7.

POSTED BY: Alexey Popkov
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract