Scientific RPN Pocket Calculator

I'm looking to build something akin to HP's non-graphing RPN calculators (15C, 42S, 32S, etc.) and am trying to come up with a bill of materials:

A 16x2 character alphanumeric LCD display ($11) Two or three 4x4 button pads ($4 each) or one pad with at least 30 buttons, if there is such a thing.

Is this feasible? How many button pads can be connected to a single Arduino? If programmed correctly, could this setup run a few dozen hours between battery changes?

Typically, a keypad is a matrix. For example, a 30 button 5x6 matrix would require 11 "connections".

With a little more electronics, you can use multiplexing to read an almost unlimited number of buttons. For example, the Arduino's ATmega chip only has one analog-to-digital converter inside, but there are several analog pins, which are read one at a time (rapidly).

I assume you are a proficient programmer to choose to do this project.

Weedpharma

A good programmer, yes, author of a widely-used open-source application, but my hardware skills are weak. Tried to solder new capacitors into a dead monitor a couple years ago, and totally hosed it.

Hi, Have you google arduino RPN calculator

https://www.youtube.com/watch?v=lBMekO3ABQU

Tom.... :)

youtube link

I see the 16x2 display, but I can't see what he's using for input.

There's also a "DIY RPN calculator" built by Richard Ottosen in 2005 complete with plastic case and a very HP-like keyboard.

For example, a 30 button 5x6 matrix would require 11 "connections".

Do they make 30-button pads? Two 4x4 pads would require 16 connections -- do any Arduinos have that many inputs?

dave_rpn: ........ Two 4x4 pads would require 16 connections -- do any Arduinos have that many inputs?

12 pins. 4 columns by 8 rows.

Weedpharma

Bearing in mind that the floats for the Arduino are only 6 - 7 digits of precision (total), and that the math library seems to lack arcsin, arccos, arctan, and probably quite few other less common scientific functions, maybe it would be better to use some 16-bit platform for this task.

Chained calculations, starting out with that poor a precision level, only get worse.

HP 32s -> best calculator ever!

I wasn't planning to use the built-in floats, but 64-bit mantissas with exponents handled separately. That's 19 decimal digits, rounded to 16 when it's time to display the answer. It won't be fast, especially with an 8-bit ALU, but fast enough for a calculator. The Teensy LC might be a good choice -- low power, low price, a 32-bit ALU, and 62K of non-volatile memory. Whatever I don't use for the firmware can hold user programs and variables.

For the keyboard, should I just buy a big bag of push-button switches, shove them into holes drilled in a sheet of plastic, and wire them together into a grid? I'm sure there's no FIFO, so I'll remember to poll the keyboard inside every loop.

Yes -- the HP 32s has roughly the feature set I'm aiming for, but with a two-line display. I've only used the 32SII and later models, but the extra features (e.g. fractions, non-RPN mode, equations, vectors) are not very useful.

Having been brought up with a HP45, and not seeing a lot of RPN calculators since, I ask why?

Weedpharma

jrdoner: Bearing in mind that the floats for the Arduino are only 6 - 7 digits of precision (total), and that the math library seems to lack arcsin, arccos, arctan, and probably quite few other less common scientific functions, maybe it would be better to use some 16-bit platform for this task.

Chained calculations, starting out with that poor a precision level, only get worse.

List of default supported math functions is quite complete and include acos, asin, atan, atan2 .. - http://www.nongnu.org/avr-libc/user-manual/group__avr__math.html

precision yes, unless one uses the big number library (don't know if that support all needed functions) - http://forum.arduino.cc/index.php?topic=85692.0

Using 6*6 keypad matrix you can make 36 key, Now have have 12 pins to deal with. Use 16*1 mux, 12 i/p of your keypad matrix and rest four to ground; Now you have 4 select lines pin to deal with.

By using arduino you can easily deal with 4 pins . :)

dave_rpn: I wasn't planning to use the built-in floats, but 64-bit mantissas with exponents handled separately. That's 19 decimal digits, rounded to 16 when it's time to display the answer.

Do you have a library for that?

I don't know any library which can handle that precision (64-bit mantissa), neither on 8-bit Atmega controllers nor on 32-bit controller platforms.

This sounds like "x86 Extended Precision Format", which is an 80-bit format in total. The only platform I'd be able to do such 80-bit "Extended" precision calculations would be my Windows PC.

Arduino natively support 64bit long long

check int64_t or uint64_t

robtillaart:
Arduino natively support 64bit long long

check int64_t or uint64_t

To what extent does that help to calculate sqrt(2) accurate up to 19 decimal digits, for example?

I think that’s the floating point accuracy about which dave_rpn wrote in reply #9 of this thread.

64 bit long long is ~19 digits so for mantissa it should be sufficient. And yes if overflow occurs digits get lost.

one can use taylor series (tuned to work in integer domain) or for sqrt you can use the inverse.

y == sqrt(x) ==> y * y == x;
OK this will probably give an 32 bit integer ~9.5 digits


To get full 64 bit precision there are other relations known about squares can be used to increase precision

x == (a + b) * (a + b) == aa + 2ab + bb [1]

so if x is 64 bit we can find (binary search) a 32 bit a so that aa <= x [2] and (a+1)(a+1) > x [3]

we can rewrite [1] to

x -aa == 2ab + bb [4]

note that a is known in the above equation.
What can we say about b?
not much directly, but we know something about x - a*a

According to [2] aa < = x
According to [3] x < a
a + 2a + 1

so 0 <= x - a*a < 2a + 1 [5]

so 0 <= 2ab + b*b < 2a + 1 [6]

as a is 32 bit ==> 2a + 1 is at most 33 bit ==> b is at most 16 bit (a smaller error remains).

this can be found with binary search again, leaving a remaining error that is 8 bit, 4 bit, 2 bit 1 bit.

adding up all those giving a 63 bit result ~19 digits.

Not trivial but doable.

proof of concept

//
//    FILE: sqrt.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// PURPOSE: proof of concept
//    DATE: 2016-01-23
//     URL: http://forum.arduino.cc/index.php?topic=373885.15
//
// Released to the public domain
//

uint32_t start;
uint32_t stop;

// sqrt(2) = 1,4142135623730950488016887242097

uint64_t x = 2000000000000000000ULL; // 2 with 18 zero's == 19 digits 
uint32_t r = 0;

void setup()
{
  Serial.begin(115200);
  Serial.print("Start ");
  Serial.println(__FILE__);

  start = micros();
  r = findSqrt(x);
  stop = micros();

  Serial.print("time:\t");
  Serial.println(stop - start);

  Serial.print("val:\t");
  Serial.println(int32_t(r));

  Serial.print("rem:\t");
  uint32_t rem = x - r * r;
  Serial.println(rem);

  // processremainder(rem, a);  //  should solve rem = 2ab +b*b
  delay(1000);

}

void loop()
{
}


uint32_t findSqrt(uint64_t x)
{
  uint64_t t = 1LL << 30;
  uint32_t r = 0;
  while (t != 0)
  {
    if ((r + t) * (r + t) <= x)
    {
      r += t;
    }
    t >>= 1;
  }
  return r;
}

output

time: 1020
val: 1414213562
rem: 1055272156

update

//
//    FILE: sqrt64.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.01
// PURPOSE: proof of concept
//    DATE: 2016-01-23
//     URL: http://forum.arduino.cc/index.php?topic=373885.15
//
// Released to the public domain
//

uint32_t start;
uint32_t stop;
//14142135623730950488016887242097

uint64_t x = 20000000000000000ULL;
uint32_t r = 0;

void setup()
{
  Serial.begin(115200);
  Serial.print("Start ");
  Serial.println(__FILE__);

  start = micros();
  r = findSqrt(x);
  stop = micros();
  Serial.print("time:\t");
  Serial.println(stop - start);
  Serial.print("val:\t");
  Serial.println(r);
  Serial.print("rem:\t");
  uint32_t rem = x - r * r;
  Serial.println(rem);
  rem = processremainder(rem, r);
  Serial.print(r);
  Serial.println(rem);
  delay(1000);

}

void loop()
{
}


uint32_t findSqrt(uint64_t x)
{
  uint64_t t = 1ULL << 31;
  uint32_t r = 0;
  while (t != 0)
  {
    if ((r + t) * (r + t) <= x)
    {
      r += t;
    }
    t >>= 1;
  }
  return r;
}

uint32_t processremainder(uint64_t rem, uint64_t a)
{
  rem *= 10000000000ULL; // add 10 0's
  a *= 100000ULL;        // add 5 0's
  uint64_t t = 1ULL << 16;
  uint64_t b = 0;
  while (t != 0)
  {
    if ( ( 2 * a * (b + t) + (b + t) * (b + t)) <= rem) b += t;
    t >>= 1;
  }
  return b;
}

output

time: 1052
val: 141421356
rem: 67121264
14142135623730

that’s about 14 digits - note value is truncated)

next step is to generalize & clean up the algorithm
[update] time is without processing remainder.

robtillaart: 14142135623730

that's about 14 digits - note value is truncated)

That's 13 correct digits after rounding away the last digit.

Thank's for confirming, that it might become a hard piece of work or requires a special high precision math library to display a result with 16 correctly rounded decimals like demanded by 'dave_rpn' in reply #9!