mirror of
https://github.com/RobTillaart/Arduino.git
synced 2024-10-03 18:09:02 -04:00
0.1.16 Fraction
This commit is contained in:
parent
8788c109fa
commit
299170332e
@ -1,4 +1,4 @@
|
||||
# Change Log fraction
|
||||
# Change Log Fraction
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
|
||||
## [0.1.16] - 2023-11-02
|
||||
- update readme.md
|
||||
- minor edits
|
||||
|
||||
|
||||
## [0.1.15] - 2023-02-02
|
||||
- update GitHub actions
|
||||
- update license 2023
|
||||
@ -14,7 +19,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- change signature **double toDouble()**
|
||||
- add **Fraction_performance.ino** to start performance testing.
|
||||
|
||||
|
||||
## [0.1.14] - 2022-11-07
|
||||
- add changelog.md
|
||||
- add rp2040 to build-CI
|
||||
|
@ -1,7 +1,7 @@
|
||||
//
|
||||
// FILE: fraction.cpp
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.1.15
|
||||
// VERSION: 0.1.16
|
||||
// PURPOSE: Arduino library to implement a Fraction data type
|
||||
// URL: https://github.com/RobTillaart/Fraction
|
||||
|
||||
@ -27,29 +27,29 @@ Fraction::Fraction(float f)
|
||||
|
||||
void Fraction::split(float f)
|
||||
{
|
||||
// handle special cases?
|
||||
// PI = 355/113; // 2.7e-7
|
||||
// PI*2 = 710/113;
|
||||
// PI/2 = 335/226;
|
||||
// EULER = 2721/1001; // 1.1e-7
|
||||
// EULER = 1264/465; // 2.2e-6
|
||||
// handle special cases?
|
||||
// PI = 355/113; // 2.7e-7
|
||||
// PI*2 = 710/113;
|
||||
// PI/2 = 335/226;
|
||||
// EULER = 2721/1001; // 1.1e-7
|
||||
// EULER = 1264/465; // 2.2e-6
|
||||
|
||||
// get robust for small values. (effectively zero)
|
||||
if (abs(f) < 0.00001)
|
||||
{
|
||||
n = 0;
|
||||
d = 1;
|
||||
return;
|
||||
}
|
||||
if (int32_t(f) == f)
|
||||
{
|
||||
n = int32_t(f);
|
||||
d = 1;
|
||||
return;
|
||||
}
|
||||
// Normalize to 0.0 ... 1.0
|
||||
bool negative = f < 0;
|
||||
if (negative) f = -f;
|
||||
// get robust for small values. (effectively zero)
|
||||
if (abs(f) < 0.00001)
|
||||
{
|
||||
n = 0;
|
||||
d = 1;
|
||||
return;
|
||||
}
|
||||
if (int32_t(f) == f)
|
||||
{
|
||||
n = int32_t(f);
|
||||
d = 1;
|
||||
return;
|
||||
}
|
||||
// Normalize to 0.0 ... 1.0
|
||||
bool negative = f < 0;
|
||||
if (negative) f = -f;
|
||||
|
||||
// TODO investigate different strategy:
|
||||
// intpart = int32_t(f); // strip of the integer part.
|
||||
@ -57,29 +57,29 @@ void Fraction::split(float f)
|
||||
// determine n, d
|
||||
// n += intpart * d; // add integer part * denominator to fraction.
|
||||
|
||||
bool reciproke = f > 1;
|
||||
if (reciproke) f = 1/f;
|
||||
bool reciproke = f > 1;
|
||||
if (reciproke) f = 1/f;
|
||||
|
||||
fractionize(f);
|
||||
simplify();
|
||||
fractionize(f);
|
||||
simplify();
|
||||
|
||||
// denormalize
|
||||
if (reciproke)
|
||||
{
|
||||
int32_t t = n;
|
||||
n = d;
|
||||
d = t;
|
||||
}
|
||||
if (negative)
|
||||
{
|
||||
n = -n;
|
||||
}
|
||||
// denormalize
|
||||
if (reciproke)
|
||||
{
|
||||
int32_t t = n;
|
||||
n = d;
|
||||
d = t;
|
||||
}
|
||||
if (negative)
|
||||
{
|
||||
n = -n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Fraction::Fraction(int32_t p, int32_t q) : n(p), d(q)
|
||||
{
|
||||
simplify();
|
||||
simplify();
|
||||
}
|
||||
|
||||
|
||||
@ -89,20 +89,20 @@ Fraction::Fraction(int32_t p, int32_t q) : n(p), d(q)
|
||||
//
|
||||
size_t Fraction::printTo(Print& p) const
|
||||
{
|
||||
size_t s = 0;
|
||||
// TODO split of sign first
|
||||
//
|
||||
// vs 22/7 => 3_1/7
|
||||
// if (n >= d)
|
||||
// {
|
||||
// s += p.print(n/d, DEC);
|
||||
// s += p.print("_");
|
||||
// }
|
||||
// s += p.print(n%d, DEC);
|
||||
s += p.print(n, DEC);
|
||||
s += p.print('/');
|
||||
s += p.print(d, DEC);
|
||||
return s;
|
||||
size_t s = 0;
|
||||
// TODO split of sign first
|
||||
//
|
||||
// vs 22/7 => 3_1/7
|
||||
// if (n >= d)
|
||||
// {
|
||||
// s += p.print(n/d, DEC);
|
||||
// s += p.print("_");
|
||||
// }
|
||||
// s += p.print(n%d, DEC);
|
||||
s += p.print(n, DEC);
|
||||
s += p.print('/');
|
||||
s += p.print(d, DEC);
|
||||
return s;
|
||||
};
|
||||
|
||||
|
||||
@ -112,7 +112,7 @@ size_t Fraction::printTo(Print& p) const
|
||||
//
|
||||
bool Fraction::operator == (const Fraction &c)
|
||||
{
|
||||
return (n * c.d) == (d * c.n);
|
||||
return (n * c.d) == (d * c.n);
|
||||
}
|
||||
|
||||
// bool Fraction::operator == (const float &f)
|
||||
@ -124,31 +124,31 @@ bool Fraction::operator == (const Fraction &c)
|
||||
|
||||
bool Fraction::operator != (const Fraction &c)
|
||||
{
|
||||
return (n * c.d) != (d * c.n);
|
||||
return (n * c.d) != (d * c.n);
|
||||
}
|
||||
|
||||
|
||||
bool Fraction::operator > (const Fraction &c)
|
||||
{
|
||||
return (n * c.d) > (d * c.n);
|
||||
return (n * c.d) > (d * c.n);
|
||||
}
|
||||
|
||||
|
||||
bool Fraction::operator >= (const Fraction &c)
|
||||
{
|
||||
return (n * c.d) >= (d * c.n);
|
||||
return (n * c.d) >= (d * c.n);
|
||||
}
|
||||
|
||||
|
||||
bool Fraction::operator < (const Fraction &c)
|
||||
{
|
||||
return (n * c.d) < (d * c.n);
|
||||
return (n * c.d) < (d * c.n);
|
||||
}
|
||||
|
||||
|
||||
bool Fraction::operator <= (const Fraction &c)
|
||||
{
|
||||
return (n * c.d) <= (d * c.n);
|
||||
return (n * c.d) <= (d * c.n);
|
||||
}
|
||||
|
||||
|
||||
@ -158,7 +158,7 @@ bool Fraction::operator <= (const Fraction &c)
|
||||
//
|
||||
Fraction Fraction::operator - ()
|
||||
{
|
||||
return Fraction(-n, d);
|
||||
return Fraction(-n, d);
|
||||
}
|
||||
|
||||
|
||||
@ -168,111 +168,111 @@ Fraction Fraction::operator - ()
|
||||
//
|
||||
Fraction Fraction::operator + (const Fraction &c)
|
||||
{
|
||||
if (d == c.d)
|
||||
{
|
||||
return Fraction(n + c.n, d);
|
||||
}
|
||||
return Fraction(n*c.d + c.n*d, d * c.d);
|
||||
if (d == c.d)
|
||||
{
|
||||
return Fraction(n + c.n, d);
|
||||
}
|
||||
return Fraction(n*c.d + c.n*d, d * c.d);
|
||||
}
|
||||
|
||||
|
||||
Fraction Fraction::operator - (const Fraction &c)
|
||||
{
|
||||
if (d == c.d)
|
||||
{
|
||||
return Fraction(n - c.n, d);
|
||||
}
|
||||
return Fraction(n*c.d - c.n*d, d * c.d);
|
||||
if (d == c.d)
|
||||
{
|
||||
return Fraction(n - c.n, d);
|
||||
}
|
||||
return Fraction(n*c.d - c.n*d, d * c.d);
|
||||
}
|
||||
|
||||
|
||||
Fraction Fraction::operator * (const Fraction &c)
|
||||
{
|
||||
return Fraction(n * c.n, d * c.d);
|
||||
return Fraction(n * c.n, d * c.d);
|
||||
}
|
||||
|
||||
|
||||
Fraction Fraction::operator / (const Fraction &c)
|
||||
{
|
||||
// division by zero returns 0
|
||||
return Fraction(n * c.d, d * c.n);
|
||||
// division by zero returns 0
|
||||
return Fraction(n * c.d, d * c.n);
|
||||
}
|
||||
|
||||
|
||||
Fraction& Fraction::operator += (const Fraction &c)
|
||||
{
|
||||
if (d == c.d)
|
||||
{
|
||||
n += c.n;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = n * c.d + c.n * d;
|
||||
d *= c.d;
|
||||
}
|
||||
simplify();
|
||||
return *this;
|
||||
if (d == c.d)
|
||||
{
|
||||
n += c.n;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = n * c.d + c.n * d;
|
||||
d *= c.d;
|
||||
}
|
||||
simplify();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Fraction& Fraction::operator -= (const Fraction &c)
|
||||
{
|
||||
if (d == c.d)
|
||||
{
|
||||
n -= c.n;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = n * c.d - c.n * d;
|
||||
d *= c.d;
|
||||
}
|
||||
simplify();
|
||||
return *this;
|
||||
if (d == c.d)
|
||||
{
|
||||
n -= c.n;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = n * c.d - c.n * d;
|
||||
d *= c.d;
|
||||
}
|
||||
simplify();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Fraction& Fraction::operator *= (const Fraction &c)
|
||||
{
|
||||
n *= c.n;
|
||||
d *= c.d;
|
||||
simplify();
|
||||
return *this;
|
||||
n *= c.n;
|
||||
d *= c.d;
|
||||
simplify();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Fraction& Fraction::operator /= (const Fraction &c)
|
||||
{
|
||||
// division by zero returns 0
|
||||
n *= c.d;
|
||||
d *= c.n;
|
||||
simplify();
|
||||
return *this;
|
||||
// division by zero returns 0
|
||||
n *= c.d;
|
||||
d *= c.n;
|
||||
simplify();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
double Fraction::toDouble()
|
||||
{
|
||||
return double(n) / d;
|
||||
return double(n) / d;
|
||||
}
|
||||
|
||||
|
||||
float Fraction::toFloat()
|
||||
{
|
||||
return float(n) / d;
|
||||
return float(n) / d;
|
||||
}
|
||||
|
||||
|
||||
// fraction is proper if abs(fraction) < 1
|
||||
bool Fraction::isProper()
|
||||
{
|
||||
return abs(n) < abs(d);
|
||||
return abs(n) < abs(d);
|
||||
}
|
||||
|
||||
|
||||
// visualize fraction as an angle in degrees
|
||||
float Fraction::toAngle()
|
||||
{
|
||||
return atan2(n, d) * (180.0 / PI);
|
||||
return atan2(n, d) * (180.0 / PI);
|
||||
}
|
||||
|
||||
|
||||
@ -305,7 +305,7 @@ int32_t Fraction::denominator()
|
||||
// at least if within precision.
|
||||
Fraction Fraction::mediant(const Fraction &a, const Fraction &b)
|
||||
{
|
||||
return Fraction(a.n + b.n, a.d + b.d);
|
||||
return Fraction(a.n + b.n, a.d + b.d);
|
||||
}
|
||||
|
||||
|
||||
@ -313,7 +313,7 @@ Fraction Fraction::mediant(const Fraction &a, const Fraction &b)
|
||||
// at least if within precision.
|
||||
Fraction Fraction::middle(const Fraction &a, const Fraction &b)
|
||||
{
|
||||
return Fraction(a.n*b.d + b.n*a.d, 2 * a.d * b.d);
|
||||
return Fraction(a.n*b.d + b.n*a.d, 2 * a.d * b.d);
|
||||
}
|
||||
|
||||
|
||||
@ -321,9 +321,9 @@ Fraction Fraction::middle(const Fraction &a, const Fraction &b)
|
||||
// sort of setDenominator(uint16_t den);
|
||||
Fraction Fraction::setDenominator(const Fraction &a, uint16_t b)
|
||||
{
|
||||
int32_t n = round((a.n * b * 1.0) / a.d);
|
||||
int32_t d = b;
|
||||
return Fraction(n, d);
|
||||
int32_t n = round((a.n * b * 1.0) / a.d);
|
||||
int32_t d = b;
|
||||
return Fraction(n, d);
|
||||
}
|
||||
|
||||
|
||||
@ -334,44 +334,44 @@ Fraction Fraction::setDenominator(const Fraction &a, uint16_t b)
|
||||
//
|
||||
int32_t Fraction::gcd(int32_t a , int32_t b)
|
||||
{
|
||||
while ( a != 0 )
|
||||
{
|
||||
int32_t c = a;
|
||||
a = b % a;
|
||||
b = c;
|
||||
}
|
||||
return b;
|
||||
while ( a != 0 )
|
||||
{
|
||||
int32_t c = a;
|
||||
a = b % a;
|
||||
b = c;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
// not that simple ...
|
||||
void Fraction::simplify()
|
||||
{
|
||||
if (n == 0)
|
||||
{
|
||||
d = 1;
|
||||
return;
|
||||
}
|
||||
bool neg = (n < 0) != (d < 0);
|
||||
int32_t p = abs(n);
|
||||
int32_t q = abs(d);
|
||||
int32_t x = gcd(p,q);
|
||||
if (n == 0)
|
||||
{
|
||||
d = 1;
|
||||
return;
|
||||
}
|
||||
bool neg = (n < 0) != (d < 0);
|
||||
int32_t p = abs(n);
|
||||
int32_t q = abs(d);
|
||||
int32_t x = gcd(p,q);
|
||||
p = p / x;
|
||||
q = q / x;
|
||||
|
||||
// denominator max 4 digits keeps mul and div simple
|
||||
// in preventing overflow
|
||||
while (q > 10000)
|
||||
{
|
||||
// rounding might need improvement
|
||||
p = (p + 5)/10;
|
||||
q = (q + 5)/10;
|
||||
x = gcd(p, q);
|
||||
p = p / x;
|
||||
q = q / x;
|
||||
|
||||
// denominator max 4 digits keeps mul and div simple
|
||||
// in preventing overflow
|
||||
while (q > 10000)
|
||||
{
|
||||
// rounding might need improvement
|
||||
p = (p + 5)/10;
|
||||
q = (q + 5)/10;
|
||||
x = gcd(p, q);
|
||||
p = p / x;
|
||||
q = q / x;
|
||||
}
|
||||
n = (neg) ? -p : p;
|
||||
d = q;
|
||||
}
|
||||
n = (neg) ? -p : p;
|
||||
d = q;
|
||||
}
|
||||
|
||||
|
||||
@ -394,50 +394,51 @@ void Fraction::simplify()
|
||||
// showed errors for very small values around 0
|
||||
void Fraction::fractionize(float val)
|
||||
{
|
||||
// find nearest fraction
|
||||
float Precision = 0.0000001;
|
||||
Fraction low(0, 1); // "A" = 0/1
|
||||
Fraction high(1, 1); // "B" = 1/1
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
float testLow = low.d * val - low.n;
|
||||
float testHigh = high.n - high.d * val;
|
||||
if (testHigh < Precision * high.d)
|
||||
break; // high is answer
|
||||
if (testLow < Precision * low.d)
|
||||
{ // low is answer
|
||||
high = low;
|
||||
break;
|
||||
}
|
||||
if (i & 1)
|
||||
{ // odd step: add multiple of low to high
|
||||
float test = testHigh / testLow;
|
||||
int32_t count = (int32_t)test; // "N"
|
||||
int32_t n = (count + 1) * low.n + high.n;
|
||||
int32_t d = (count + 1) * low.d + high.d;
|
||||
if ((n > 0x8000) || (d > 0x10000))
|
||||
break;
|
||||
high.n = n - low.n; // new "A"
|
||||
high.d = d - low.d;
|
||||
low.n = n; // new "B"
|
||||
low.d = d;
|
||||
}
|
||||
else
|
||||
{ // even step: add multiple of high to low
|
||||
float test = testLow / testHigh;
|
||||
int32_t count = (int32_t)test; // "N"
|
||||
int32_t n = low.n + (count + 1) * high.n;
|
||||
int32_t d = low.d + (count + 1) * high.d;
|
||||
if ((n > 0x10000) || (d > 0x10000))
|
||||
break;
|
||||
low.n = n - high.n; // new "A"
|
||||
low.d = d - high.d;
|
||||
high.n = n; // new "B"
|
||||
high.d = d;
|
||||
}
|
||||
// find nearest fraction
|
||||
float Precision = 0.0000001;
|
||||
Fraction low(0, 1); // "A" = 0/1
|
||||
Fraction high(1, 1); // "B" = 1/1
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
float testLow = low.d * val - low.n;
|
||||
float testHigh = high.n - high.d * val;
|
||||
if (testHigh < Precision * high.d)
|
||||
break; // high is answer
|
||||
|
||||
if (testLow < Precision * low.d)
|
||||
{ // low is answer
|
||||
high = low;
|
||||
break;
|
||||
}
|
||||
n = high.n;
|
||||
d = high.d;
|
||||
if (i & 1)
|
||||
{ // odd step: add multiple of low to high
|
||||
float test = testHigh / testLow;
|
||||
int32_t count = (int32_t)test; // "N"
|
||||
int32_t n = (count + 1) * low.n + high.n;
|
||||
int32_t d = (count + 1) * low.d + high.d;
|
||||
if ((n > 0x8000) || (d > 0x10000))
|
||||
break;
|
||||
high.n = n - low.n; // new "A"
|
||||
high.d = d - low.d;
|
||||
low.n = n; // new "B"
|
||||
low.d = d;
|
||||
}
|
||||
else
|
||||
{ // even step: add multiple of high to low
|
||||
float test = testLow / testHigh;
|
||||
int32_t count = (int32_t)test; // "N"
|
||||
int32_t n = low.n + (count + 1) * high.n;
|
||||
int32_t d = low.d + (count + 1) * high.d;
|
||||
if ((n > 0x10000) || (d > 0x10000))
|
||||
break;
|
||||
low.n = n - high.n; // new "A"
|
||||
low.d = d - high.d;
|
||||
high.n = n; // new "B"
|
||||
high.d = d;
|
||||
}
|
||||
}
|
||||
n = high.n;
|
||||
d = high.d;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,15 +2,14 @@
|
||||
//
|
||||
// FILE: fraction.h
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.1.15
|
||||
// VERSION: 0.1.16
|
||||
// PURPOSE: Arduino library to implement a Fraction data type
|
||||
// URL: https://github.com/RobTillaart/Fraction
|
||||
//
|
||||
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#define FRACTION_LIB_VERSION (F("0.1.15"))
|
||||
#define FRACTION_LIB_VERSION (F("0.1.16"))
|
||||
|
||||
|
||||
class Fraction: public Printable
|
||||
|
@ -15,8 +15,8 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/RobTillaart/Fraction.git"
|
||||
},
|
||||
"version": "0.1.15",
|
||||
"frameworks": "arduino",
|
||||
"version": "0.1.16",
|
||||
"frameworks": "*",
|
||||
"platforms": "*",
|
||||
"headers": "fraction.h"
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
name=Fraction
|
||||
version=0.1.15
|
||||
version=0.1.16
|
||||
author=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
sentence=Arduino library to implement a Fraction datatype
|
||||
sentence=Arduino library to implement a Fraction datatype.
|
||||
paragraph=Nominator and denominator are limited to 4 digits. Experimental.
|
||||
category=Data Processing
|
||||
url=https://github.com/RobTillaart/Fraction
|
||||
|
@ -2,8 +2,11 @@
|
||||
[![Arduino CI](https://github.com/RobTillaart/Fraction/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
|
||||
[![Arduino-lint](https://github.com/RobTillaart/Fraction/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/Fraction/actions/workflows/arduino-lint.yml)
|
||||
[![JSON check](https://github.com/RobTillaart/Fraction/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/Fraction/actions/workflows/jsoncheck.yml)
|
||||
[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/Fraction.svg)](https://github.com/RobTillaart/Fraction/issues)
|
||||
|
||||
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/Fraction/blob/master/LICENSE)
|
||||
[![GitHub release](https://img.shields.io/github/release/RobTillaart/Fraction.svg?maxAge=3600)](https://github.com/RobTillaart/Fraction/releases)
|
||||
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/Fraction.svg)](https://registry.platformio.org/libraries/robtillaart/Fraction)
|
||||
|
||||
|
||||
# Fraction
|
||||
@ -51,7 +54,7 @@ In short, use fractions with care otherwise your sketch might get broken ;)
|
||||
|
||||
#### Printable
|
||||
|
||||
The Fraction library implements Printable, so one can do.
|
||||
The Fraction library implements the Printable interface, so one can do.
|
||||
|
||||
```cpp
|
||||
Fraction fr(PI);
|
||||
@ -102,7 +105,6 @@ The library is reasonably tested. If problems arise please open an issue.
|
||||
#### Should
|
||||
|
||||
- performance testing
|
||||
|
||||
- investigate divide by zero errors
|
||||
- NAN in fraction? => 0/0 ?
|
||||
- INF in fraction? => 1/0 and -1/0?
|
||||
@ -120,3 +122,12 @@ The library is reasonably tested. If problems arise please open an issue.
|
||||
|
||||
#### Wont
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
If you appreciate my libraries, you can support the development and maintenance.
|
||||
Improve the quality of the libraries by providing issues and Pull Requests, or
|
||||
donate through PayPal or GitHub sponsors.
|
||||
|
||||
Thank you,
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user