Pounds, Shillings, Pence

April 7, 2015

Let’s take as an example the pounds-shillings-pence system (20 12). We express a number as (7 12 3) meaning 7 poungs, 12 shillings, and 3 pence. The sum of (7 12 3) and (8 14 10) is then (16 7 1), since 3 pence and 10 pence form 1 shilling and 1 penny, 12 shillings and 14 shillings, plus the 1 shilling carried over from the pence calculation, for 1 pound and 7 shillings, and 7 pounds and 8 pounds, plus the 1 pound carried over from the shilling calculation, form 16 pounds. We never write (15 26 13), even though that is the position-by-position sum, because that is considered incorrect form, so our first function normalizes a number:

```(define (normalize schema number)   (let loop ((ss (reverse schema))              (ns (reverse number))              (result (list)) (carry 0))     (drop-while zero?       (cond ((null? ns)               (if (zero? carry) result                 (cons carry result)))             ((null? ss)               (cons (+ carry (car ns)) result))             (else (loop (cdr ss) (cdr ns)               (cons (modulo (+ (car ns) carry) (car ss)) result)               (let* ((x (+ (car ns) carry))                      (q (quotient x (car ss)))                      (r (remainder x (car ss))))                 (if (negative? r) (- q 1) q))))))))```

This is tricky, for a couple of reasons. First, Scheme rounds the quotient toward zero, which does the wrong thing when one of the elements of the number is negative. Second, we want to suppress leading zeros. For instance:

```> (normalize '(20 12) '(15 26 13)) (16 7 1) > (normalize '(20 12) '(1 -2 7)) (18 7)```

Then addition or subtraction just proceeds element-wise through the number-lists and normalizes at the end; we can’t say `(normalize schema (map op x y))` because the numbers might have varying length:

```(define (plus/minus schema op x y)   (let loop ((xs (reverse x))              (ys (reverse y))              (result (list)))     (cond ((and (null? xs) (null? ys))             (normalize schema result))           ((null? xs)             (loop xs (cdr ys) (cons (car ys) result)))           ((null? ys)             (loop (cdr xs) ys (cons (car xs) result)))           (else (loop (cdr xs) (cdr ys)             (cons (op (car xs) (car ys)) result))))))```

Here are some examples:

```> (plus/minus '(20 12) + '(8 12 10) '(7 14 3)) (16 7 1) > (plus/minus '(20 12) - '(8 12 10) '(7 14 3)) (18 7) > (plus/minus '(20 12) - '(7 14 3) '(8 12 10)) (-1 1 5)```

We used `drop-while` from the Standard Prelude. You can run the program at http://ideone.com/yojsBB.

Pages: 1 2

9 Responses to “Pounds, Shillings, Pence”

1. Rutger said

In Python.

```
for i in range(len(radices)-1, -1, -1):
return result

result = 0
return result

if __name__ == "__main__":
# weeks, days, hours, minutes, seconds

# 1 second less than 1 week
number = 7*24*60*60-1

# 1 second less than 1 hour

# 1000 pounds, 20 shilling, 12 pence
maximum = 1
maximum *= v
# some random tests
import random
for i in range(10000):
number = random.randint(1, maximum)
```
2. Rutger said

fix markup?

```def number_to_mixed_radix(number, radices):
for i in range(len(radices)-1, -1, -1):
return result

result = 0
return result

if __name__ == "__main__":
# weeks, days, hours, minutes, seconds

# 1 second less than 1 week
number = 7*24*60*60-1

# 1 second less than 1 hour

# 1000 pounds, 20 shilling, 12 pence
maximum = 1
maximum *= v
# some random tests
import random
for i in range(10000):
number = random.randint(1, maximum)
```
3. Rutger said

really

```def number_to_mixed_radix(number, radices):
for i in range(len(radices)-1, -1, -1):
return result

result = 0
return result

if __name__ == "__main__":
# weeks, days, hours, minutes, seconds

# 1 second less than 1 week
number = 7*24*60*60-1

# 1 second less than 1 hour

# 1000 pounds, 20 shilling, 12 pence
maximum = 1
maximum *= v
# some random tests
import random
for i in range(10000):
number = random.randint(1, maximum)
```
4. matthew said

Nice little problem. Here’s a solution using C arrays, handling carries and borrows as in normal decimal addition. Input can be non-normalized, but the output is always normalized (so adding or subtracting zero normalizes a number). Least significant digit first (though I’ve printed the digits in the opposite order):

```#include <stdio.h>

int radixadd(int *a, const int *b, const int *r, int n) {
for (int c = 0, i = 0; ; i++) {
int k = a[i]+b[i]+c;
if (i < n) {
c = k/r[i]; a[i] = k%r[i];
} else {
a[n] = k; break;
}
}
}

int radixsub(int *a, const int *b, const int *r, int n) {
for (int c = 0, i = 0; ; i++) {
int k = a[i]-b[i]+c;
if (i < n) {
c = k/r[i]; a[i] = k%r[i];
if (a[i] < 0) {
a[i] += r[i]; c--; // modulus should be positive
}
} else {
a[n] = k; break;
}
}
}

int total(int *a, const int *r, int n) {
int c = a[n];
for (int i = n; i > 0; i--) {
c = r[i-1]*c + a[i-1];
}
return c;
}

int main()
{
int r[] = { 12, 20 };
int zero[] = { 0, 0, 0 };
int a[] = { 1729, 0, 0 };
int b[] = { 250, 0, 0 };
printf("%d %d %d: %d\n", a[2],a[1],a[0], total(a,r,2));
printf("%d %d %d: %d\n", b[2],b[1],b[0], total(b,r,2));
printf("%d %d %d: %d\n", a[2],a[1],a[0], total(a,r,2));
radixsub(a,b,r,2); // Sub b from a
radixsub(a,b,r,2); // Sub b from a
printf("%d %d %d: %d\n", a[2],a[1],a[0], total(a,r,2));
radixsub(b,a,r,2); // Sub a from b
printf("%d %d %d: %d\n", b[2],b[1],b[0], total(b,r,2));
printf("%d %d %d: %d\n", b[2],b[1],b[0], total(b,r,2));
}
```
5. matthew said

6. Krishnan R said

Solution in Java. However, leading zero’s are not removed..

```package com.experiment;

public class PoundsShillingsPence {

private static int[] result;
public static void main(String[] args) {
operate('+', new int[] {20, 12}, new int[] {8, 12, 10}, new int[] {7, 14, 3});
operate('-', new int[] {20, 12}, new int[] {8, 12, 10}, new int[] {7, 14, 3});
operate('-', new int[] {20, 12}, new int[] {7, 14, 3}, new int[] {8, 12, 10});
}

private static void normalize(int[] scheme, int[] input) {
int mod, s, i, carry=0;
result = new int[input.length];
for(s = scheme.length-1, i = input.length-1;i>=0;i--,s--) {
if(s>=0) {
mod = (input[i]+carry)%scheme[s];
carry = (input[i]+carry)/scheme[s];
if(mod<0) {
mod = mod+scheme[s];
carry = carry-1;
}
result[i] = mod;
} else {
result[i] = (input[i]+carry);
}
}
System.out.print("{");
for(int l=0;l<result.length;l++) {
System.out.print(result[l]);
if(l!=result.length-1)
System.out.print(",");
}
System.out.print("}\n");
}

private static void operate(char operator, int[] schema, int[] input1, int[] input2) {
int i1 = input1.length;
int i2 = input2.length;
int r = i1<i2?i2:i1;
int result[] = new int[r];

for(i1=i1-1,i2=i2-1,r=r-1;;i1--,i2--,r--) {
if(i1<0 && i2<0) {
break;
}
if(i1<0) {
result[i2] = input2[i2];
continue;
}
if(i2<0) {
result[i1] = input1[i1];
continue;
}
if(operator=='+') {
result[r] = input1[i1]+input2[i2];
}
else if(operator=='-')
result[r] = input1[i1]-input2[i2];
else {
System.out.println("Invalid operator. Only + and - are supported");
return;
}
}
normalize(schema, result);
}
}
```
7. Dennis Decker Jensen said

An example in ISO Prolog (gprolog):

:- initialization(main).

main :-
% Weeks, days, hours, minutes, seconds
Number is 7*24*60*60-1, % 1 second less than 1 week
format(‘~d weeks, ~d days, ~d hours, ~d minutes, and ~d seconds~n’, Ns),
Hourm1sec = [0,0,1,0,-1], % 1 second less than 1 hour
format(‘1 second less than 1 hour ~w = ~w = ~d seconds~n’,
[Hourm1sec,Nd,Secs]),
Ym1sec = [364,0,0,-1], % 1 year – 1 second, but 7*52 = 364
format(‘1 second less than 1 year in days and time: ~w~n’,[Ym1sec]),
format(‘1 second less than 1 year in weeks, days and time: ~w~n’,
[Yw]),
format(‘ 7 weekdays * 52 weeks in days: 364~n’,[]),
format(‘1 second less than 1 year in seconds: ~d~n’,[S]),
% Pounds, shillings, pence
format(‘~d £, ~d shillings, and ~d pence are ~d pence total~n’,
[8,12,10,Pence]),
format(‘~d pence are ~d £, ~d shillings, and ~d pence~n’,[Pence|Ps]),
% Degrees, minutes, seconds
format(’53 degrees 45”20”” W is ~d seconds west~n’,[Seconds]),
% Binary numbers
format(’67 in a binary (octet) is ~w~n’,[B]),
format(‘7F hexadecimal is ~d decimal~n’, [Hx]),
format(‘254 decimal is ~w hexadecimal~n’, [Hx1]),
halt.

mixed_radix_number(Rs, Ns, S) :- nonvar(Ns), !,
reverse(Rs, Rs1),

S1 is R * S0 + N,

Rem is S mod R,
S1 is floor(S / R),
[/source/

8. Dennis Decker Jensen said

Alas, try again to get formatting right:

```:- initialization(main).

main :-
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;% Weeks, days, hours, minutes, seconds
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Number is 7*24*60*60-1, % 1 second less than 1 week
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format('~d weeks, ~d days, ~d hours, ~d minutes, and ~d seconds~n', Ns),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hourm1sec = [0,0,1,0,-1], % 1 second less than 1 hour
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format('1 second less than 1 hour ~w = ~w = ~d seconds~n',
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Hourm1sec,Nd,Secs]),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ym1sec = [364,0,0,-1], % 1 year - 1 second, but 7*52 = 364
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format('1 second less than 1 year in days and time:        ~w~n',[Ym1sec]),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format('1 second less than 1 year in weeks, days and time: ~w~n',
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Yw]),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format('                    7 weekdays * 52 weeks in days: 364~n',[]),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format('1 second less than 1 year in seconds:              ~d~n',[S]),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;% Pounds, shillings, pence
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format('~d £, ~d shillings, and ~d pence are ~d pence total~n',
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[8,12,10,Pence]),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format('~d pence are ~d £, ~d shillings, and ~d pence~n',[Pence|Ps]),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;% Degrees, minutes, seconds
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format('53 degrees 45''20'''' W is ~d seconds west~n',[Seconds]),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;% Binary numbers
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format('67 in a binary (octet) is ~w~n',[B]),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format('7F hexadecimal is ~d decimal~n', [Hx]),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format('254 decimal is ~w hexadecimal~n', [Hx1]),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;halt.

mixed_radix_number(Rs, Ns, S) :- nonvar(Ns), !,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reverse(Rs, Rs1),

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;S1 is R * S0 + N,

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rem is S mod R,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;S1 is floor(S / R),
```
9. Dennis Decker Jensen said

Have mercy, this does not bode well, 3rd time:

```:- initialization(main).

main :-
% Weeks, days, hours, minutes, seconds
Number is 7*24*60*60-1, % 1 second less than 1 week
format('~d weeks, ~d days, ~d hours, ~d minutes, and ~d seconds~n', Ns),
Hourm1sec = [0,0,1,0,-1], % 1 second less than 1 hour
format('1 second less than 1 hour ~w = ~w = ~d seconds~n',
[Hourm1sec,Nd,Secs]),
Ym1sec = [364,0,0,-1], % 1 year - 1 second, but 7*52 = 364
format('1 second less than 1 year in days and time:        ~w~n',[Ym1sec]),
format('1 second less than 1 year in weeks, days and time: ~w~n',
[Yw]),
format('                    7 weekdays * 52 weeks in days: 364~n',[]),
format('1 second less than 1 year in seconds:              ~d~n',[S]),
% Pounds, shillings, pence
format('~d £, ~d shillings, and ~d pence are ~d pence total~n',
[8,12,10,Pence]),
format('~d pence are ~d £, ~d shillings, and ~d pence~n',[Pence|Ps]),
% Degrees, minutes, seconds
format('53 degrees 45''20'''' W is ~d seconds west~n',[Seconds]),
% Binary numbers
format('67 in a binary (octet) is ~w~n',[B]),
format('7F hexadecimal is ~d decimal~n', [Hx]),
format('254 decimal is ~w hexadecimal~n', [Hx1]),
halt.

mixed_radix_number(Rs, Ns, S) :- nonvar(Ns), !,
reverse(Rs, Rs1),

S1 is R * S0 + N,

Rem is S mod R,
S1 is floor(S / R),