J. Doe
J. Doe

Reputation: 35

Algorithm for converting decimal fractions to negadecimal?

I would like to know, how to convert fractional values (say, -.06), into negadecimal or a negative base. I know -.06 is .14 in negadecimal, because I can do it the other way around, but the regular algorithm used for converting fractions into other bases doesn't work with a negative base. Dont give a code example, just explain the steps required.

The regular algorithm works like this: You times the value by the base you're converting into. Record whole numbers, then keep going with the remaining fraction part until there is no more fraction:

0.337 in binary:

0.337*2 = 0.674 "0"

0.674*2 = 1.348 "1"

0.348*2 = 0.696 "0"

0.696*2 = 1.392 "1"

0.392*2 = 0.784 "0"

0.784*2 = 1.568 "1"

0.568*2 = 1.136 "1"

Approximately .0101011

Upvotes: 2

Views: 881

Answers (3)

Panagiotis Melios
Panagiotis Melios

Reputation: 11

For your question i thought about this object-oriented code. I am not sure although. This class takes two negadecimals numbers with an operator and creates an equation, then converts those numbers to decimals.

public class NegadecimalNumber {
private int number1;
private char operator;
private int number2;

public NegadecimalNumber(int a, char op, int b) {
    this.number1 = a;
    this.operator = op;
    this.number2 = b;

}

public int ConvertNumber1(int a) {
    int i = 1;
    int nega, temp;
    temp = a;
    int n = a & (-10);
    while (n > 0) {
        temp = a / (-10);
        n = temp % (-10);
        n = n * i;
        i = i * 10;
    }
    nega = n;
    return nega;
}

public int ConvertNumber2(int b) {
    int i = 1;
    int negb, temp;
    temp = b;
    int n = b & (-10);
    while (n > 0) {
        temp = b / (-10);
        n = temp % (-10);
        n = n * i;
        i = i * 10;
    }
    negb = n;
    return negb;
}

public double Equation() {
    double ans = 0;
    if (this.operator == '+') {
        ans = this.number1 + this.number2;
    } else if (this.operator == '-') {
        ans = this.number1 - this.number2;
    } else if (this.operator == '*') {
        ans = this.number1 * this.number2;
    } else if (this.operator == '/') {
        ans = this.number1 / this.number2;
    }
    return ans;
}

}

Upvotes: 1

templatetypedef
templatetypedef

Reputation: 373082

I have a two-step algorithm for doing the conversion. I'm not sure if this is the optimal algorithm, but it works pretty well.

The basic idea is to start off by getting a decimal representation of the number, then converting that decimal representation into a negadecimal representation by handling the even powers and odd powers separately.

Here's an example that motivates the idea behind the algorithm. This is going to go into a lot of detail, but ultimately will arrive at the algorithm and at the same time show where it comes from.

Suppose we want to convert the number 0.523598734 to negadecimal (notice that I'm presupposing you can convert to decimal). Notice that

0.523598734 =   5 * 10^-1
              + 2 * 10^-2
              + 3 * 10^-3
              + 5 * 10^-4
              + 9 * 10^-5
              + 8 * 10^-6
              + 7 * 10^-7
              + 3 * 10^-8
              + 4 * 10^-9

Since 10^-n = (-10)^-n when n is even, we can rewrite this as

0.523598734 =   5 * 10^-1
              + 2 * (-10)^-2
              + 3 * 10^-3
              + 5 * (-10)^-4
              + 9 * 10^-5
              + 8 * (-10)^-6
              + 7 * 10^-7
              + 3 * (-10)^-8
              + 4 * 10^-9

Rearranging and regrouping terms gives us this:

0.523598734 =   2 * (-10)^-2
              + 5 * (-10)^-4
              + 8 * (-10)^-6
              + 3 * (-10)^-8
              + 5 * 10^-1
              + 3 * 10^-3
              + 9 * 10^-5
              + 7 * 10^-7
              + 4 * 10^-9

If we could rewrite those negative terms as powers of -10 rather than powers of 10, we'd be done. Fortunately, we can make a nice observation: if d is a nonzero digit (1, 2, ..., or 9), then

  d * 10^-n + (10 - d) * 10^-n
= 10^-n (d + 10 - d)
= 10^-n (10)
= 10^{-n+1}

Restated in a different way:

  d * 10^-n + (10 - d) * 10^-n = 10^{-n+1}

Therefore, we get this useful fact:

  d * 10^-n = 10^{-n+1} - (10 - d) * 10^-n

If we assume that n is odd, then -10^-n = (-10)^-n and 10^{-n+1} = (-10)^{-n+1}. Therefore, for odd n, we see that

  d * 10^-n = 10^{-n+1}    - (10 - d) * 10^-n
            = (-10)^{-n+1} + (10 - d) * (-10)^-n

Think about what this means in a negadecimal setting. We've turned a power of ten into a sum of two powers of minus ten.

Applying this to our summation gives this:

0.523598734 =   2 * (-10)^-2
              + 5 * (-10)^-4
              + 8 * (-10)^-6
              + 3 * (-10)^-8
              + 5 * 10^-1
              + 3 * 10^-3
              + 9 * 10^-5
              + 7 * 10^-7
              + 4 * 10^-9
            =   2 * (-10)^-2
              + 5 * (-10)^-4
              + 8 * (-10)^-6
              + 3 * (-10)^-8
              + (-10)^0  + 5 * (-10)^-1
              + (-10)^-2 + 7 * (-10)^-3
              + (-10)^-4 + 1 * (-10)^-5
              + (-10)^-6 + 3 * (-10)^-7
              + (-10)^-8 + 6 * (-10)^-9

Regrouping gives this:

0.523598734 = (-10)^0
              + 5 * (-10)^-1
              + 2 * (-10)^-2 + (-10)^-2
              + 7 * (-10)^-3
              + 5 * (-10)^-4 + (-10)^-4
              + 1 * (-10)^-5
              + 8 * (-10)^-6 + (-10)^-6
              + 3 * (-10)^-7
              + 3 * (-10)^-8 + (-10)^-8
              + 6 * (-10)^-9

Overall, this gives a negadecimal representation of 1.537619346ND

Now, let's think about this at a negadigit level. Notice that

  • Digits in even-numbered positions are mostly preserved.
  • Digits in odd-numbered positions are flipped: any nonzero, odd-numbered digit is replaced by 10 minus that digit.
  • Each time an odd-numbered digit is flipped, the preceding digit is incremented.

Let's look at 0.523598734 and apply this algorithm directly. We start by flipping all of the odd-numbered digits to give their 10's complement:

0.523598734 --> 0.527518336

Next, we increment the even-numbered digits preceding all flipped odd-numbered digits:

0.523598734 --> 0.527518336 --> 1.537619346ND

This matches our earlier number, so it looks like we have the makings of an algorithm!

Things get a bit trickier, unfortunately, when we start working with decimal values involving the number 9. For example, let's take the number 0.999. Applying our algorithm, we start by flipping all the odd-numbered digits:

0.999 --> 0.191

Now, we increment all the even-numbered digits preceding a column that had a value flipped:

0.999 --> 0.191 --> 1.1(10)1

Here, the (10) indicates that the column containing a 9 overflowed to a 10. Clearly this isn't allowed, so we have to fix it.

To figure out how to fix this, it's instructive to look at how to count in negabinary. Here's how to count from 0 to 110:

000
001
002
003
...
008
009
190
191
192
193
194
...
198
199
180
181
...
188
189
170
...
118
119
100
101
102
...
108
109
290

Fortunately, there's a really nice pattern here. The basic mechanism works like normal base-10 incrementing: increment the last digit, and if it overflows, carry a 1 into the next column, continuing to carry until everything stabilizes. The difference here is that the odd-numbered columns work in reverse. If you increment the -10s digit, for example, you actually subtract one rather than adding one, since increasing the value in that column by 10 corresponds to having one fewer -10 included in your sum. If that number underflows at 0, you reset it back to 9 (subtracting 90), then increment the next column (adding 100). In other words, the general algorithm for incrementing a negadecimal number works like this:

  • Start at the 1's column.
  • If the current column is at an even-numbered position:
    • Add one.
    • If the value reaches 10, set it to zero, then apply this procedure to the preceding column.
  • If the current column is at an odd-numbered position:
    • Subtract one.
    • If the values reaches -1, set it to 9, then apply this procedure to the preceding column.

You can confirm that this math works by generalizing the above reasoning about -10s digits and 100s digits and realizing that overflowing an even-numbered column corresponding to 10k means that you need to add in 10k+1, which means that you need to decrement the previous column by one, and that underflowing an odd-numbered column works by subtracting out 9 · 10k, then adding in 10k+1.

Let's go back to our example at hand. We're trying to convert 0.999 into negadecimal, and we've gotten to

0.999 --> 0.191 --> 1.1(10)1

To fix this, we'll take the 10's column and reset it back to 0, then carry the 1 into the previous column. That's an odd-numbered column, so we decrement it. This gives the final result:

0.999 --> 0.191 --> 1.1(10)1 --> 1.001ND

Overall, for positive numbers, we have the following algorithm for doing the conversion:

  • Processing digits from left to right:
    • If you're at an odd-numbered digit that isn't zero:
      • Replace the digit d with the digit 10 - d.
      • Using the standard negadecimal addition algorithm, increment the value in the previous column.

Of course, negative numbers are a whole other story. With negative numbers, the odd columns are correct and the even columns need to be flipped, since the parity of the (-10)k terms in the summation flip. Consequently, for negative numbers, you apply the above algorithm, but preserve the odd columns and flip the even columns. Similarly, instead of incrementing the preceding digit when doing a flip, you decrement the preceding digit.

As an example, suppose we want to convert -0.523598734 into negadecimal. Applying the algorithm gives this:

-0.523598734 --> 0.583592774 --> 0.6845(10)2874 --> 0.684402874ND

This is indeed the correct representation.

Hope this helps!

Upvotes: 1

mcdowella
mcdowella

Reputation: 19621

Note that https://en.wikipedia.org/wiki/Negative_base#To_Negative_Base tells you how to convert whole numbers to a negative base. So one way to solve the problem is simply to multiply the fraction by a high enough power of 100 to turn it into a whole number, convert, and then divide again: -0.06 = -6 / 100 => 14/100 = 0.14.

Another way is to realise that you are trying to create a sum of the form -a/10 + b/100 -c/1000 + d/10000... to approximate the target number so you want to reduce the error as much as possible at each stage, but you need to leave an error in the direction that you can correct at the next stage. Note that this also means that a fraction might not start with 0. when converted. 0.5 => 1.5 = 1 - 5/10.

So to convert -0.06. This is negative and the first digit after the decimal point is in the range [0.0, -0.1 .. -0.9] so we start with 0. to leave us -0.06 to convert. Now if the first digit after the decimal point is 0 then I have -0.06 left, which is in the wrong direction to convert with 0.0d so I need to chose the first digit after the decimal point to produce an approximation below my target -0.06. So I chose 0.1, which is actually -0.1 and leaves me with an error of 0.04, which I can convert exactly leaving me the conversion of 0.14.

So at each point output the digit which gives you either

1) The exact result, in which case you are finished

2) An approximation which is slightly larger than the target number, if the next digit will be negative.

3) An approximation which is slightly smaller than the target number, if the next digit will be positive.

And if you start off trying to approximate a number in the range (-1.0, 0.0] at each point you can choose a digit which keeps the remaining error small enough and in the right direction, so this always works.

Upvotes: 0

Related Questions