Beispiel #1
0
		public static FractionValue DoubleToFraction(double value)
		{
			// I used to always do "new FractionValue((decimal)value)" because
			// it does a good job for fixed length fractions.  But it made no attempt
			// to deal with repeating fractions, which are very common, so I've
			// pulled in the algorithm I used in my old RPN Calc 2.  Now I try both
			// algorithms and then see which appears to be better.
			FractionValue local = DoubleToRationalLocal(value);
			double localDouble = local.ToDouble();

			// Use FractionValue's algorithm.
			FractionValue fraction = new((decimal)value);
			double fractionDouble = fraction.ToDouble();

			// See how far both are off from the original value.
			double localEpsilon = Math.Abs(localDouble - value);
			double fractionEpsilon = Math.Abs(fractionDouble - value);

			// Use the number of digits too.  If I divide 5.0/7.0 and then
			// convert back to a fraction, then the local algorithm nails it.
			// But if I enter a value of "0.714285714285714", then local's
			// epsilon is a tad farther off, even though it comes up with a
			// much smaller and better denominator.
			int localDenomDigits = NumDigits(local.Denominator);
			int fracDenomDigits = NumDigits(fraction.Denominator);
			int diffDigits = Math.Abs(localDenomDigits - fracDenomDigits);

			FractionValue result;
			if (diffDigits <= 2)
			{
				// The denominator sizes are close, so choose the result based on which
				// epsilon is smaller.  This is important for "0.13333", which produces fewer
				// denominator digits with the local algorithm but a much larger epsilon.
				result = localEpsilon < fractionEpsilon ? local : fraction;
			}
			else
			{
				// The denominator sizes are several orders of magnitude different, so
				// choose the one with the fewest digits.  The FractionValue algorithm
				// can always get a small epsilon by using a huge denominator, but that
				// rarely produces a fraction the user actually wants.
				result = localDenomDigits < fracDenomDigits ? local : fraction;
			}

			return result;
		}
Beispiel #2
0
        public void DtoF(Command cmd)
        {
            this.RequireArgs(1);
            this.RequireScalarNumericType(0);
            var          value  = (NumericValue)cmd.UseTopValue();
            NumericValue result = value;

            switch (value.ValueType)
            {
            case RpnValueType.Binary:
                result = new FractionValue(((BinaryValue)value).ToInteger(), BigInteger.One);
                break;

            case RpnValueType.Integer:
                result = new FractionValue(((IntegerValue)value).AsInteger, BigInteger.One);
                break;

            case RpnValueType.Double:
                result = Utility.DoubleToFraction(((DoubleValue)value).AsDouble);
                break;
            }

            cmd.Commit(result);
        }
Beispiel #3
0
        private static IEnumerable <Value> ValidateAndReduce(Value[] valuesToPush)
        {
            IEnumerable <Value> result;

            int numValuesToPush = valuesToPush.Length;

            if (numValuesToPush == 0)
            {
                result = valuesToPush;
            }
            else
            {
                // This method does value type reduction, which is something
                // the HP48 doesn't do.  It's usually a good thing:
                //  * Complex values with 0 imaginary part are reduced to doubles.
                //  * Doubles with no fractional part are reduced to integers.
                //  * Fractions with a denominator of 1 are reduced to integers.
                //
                // But sometimes the results can seem odd.  For example:
                //  * If I manually type in "(4,0)" it ends up as the integer 4.
                //  * If 4 and 0 are on the stack, then RtoC returns 4.
                //
                // I'm going to keep this logic in spite of the oddities though
                // because numeric value types will be implicitly converted up
                // (i.e., widened) whenever necessary in operations.
                List <Value> values = new(numValuesToPush);
                foreach (Value value in valuesToPush)
                {
                    switch (value.ValueType)
                    {
                    case RpnValueType.Complex:
                        ComplexValue complexValue = (ComplexValue)value;
                        Complex      complex      = complexValue.AsComplex;
                        Validate(complex.Real);
                        Validate(complex.Imaginary);

                        // Reduce to a double or integer if the imaginary part is 0.
                        values.Add(Reduce(complex, complexValue));
                        break;

                    case RpnValueType.Double:
                        DoubleValue doubleValue = (DoubleValue)value;
                        double      dbl         = doubleValue.AsDouble;
                        Validate(dbl);

                        // Reduce to an integer if there's no fractional part.
                        values.Add(Reduce(dbl, doubleValue));
                        break;

                    case RpnValueType.Fraction:
                        FractionValue fraction = (FractionValue)value;

                        // Reduce to an integer if the denominator is 1.
                        if (fraction.Denominator == BigInteger.One)
                        {
                            values.Add(new IntegerValue(fraction.Numerator));
                        }
                        else
                        {
                            values.Add(value);
                        }

                        break;

                    default:
                        values.Add(value);
                        break;
                    }
                }

                result = values;
            }

            return(result);
        }