0.1.16 Fraction

This commit is contained in:
Rob Tillaart 2023-11-02 15:10:48 +01:00
parent 8788c109fa
commit 299170332e
6 changed files with 212 additions and 197 deletions

View File

@ -1,4 +1,4 @@
# Change Log fraction # Change Log Fraction
All notable changes to this project will be documented in this file. 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/). 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 ## [0.1.15] - 2023-02-02
- update GitHub actions - update GitHub actions
- update license 2023 - update license 2023
@ -14,7 +19,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- change signature **double toDouble()** - change signature **double toDouble()**
- add **Fraction_performance.ino** to start performance testing. - add **Fraction_performance.ino** to start performance testing.
## [0.1.14] - 2022-11-07 ## [0.1.14] - 2022-11-07
- add changelog.md - add changelog.md
- add rp2040 to build-CI - add rp2040 to build-CI

View File

@ -1,7 +1,7 @@
// //
// FILE: fraction.cpp // FILE: fraction.cpp
// AUTHOR: Rob Tillaart // AUTHOR: Rob Tillaart
// VERSION: 0.1.15 // VERSION: 0.1.16
// PURPOSE: Arduino library to implement a Fraction data type // PURPOSE: Arduino library to implement a Fraction data type
// URL: https://github.com/RobTillaart/Fraction // URL: https://github.com/RobTillaart/Fraction
@ -27,29 +27,29 @@ Fraction::Fraction(float f)
void Fraction::split(float f) void Fraction::split(float f)
{ {
// handle special cases? // handle special cases?
// PI = 355/113; // 2.7e-7 // PI = 355/113; // 2.7e-7
// PI*2 = 710/113; // PI*2 = 710/113;
// PI/2 = 335/226; // PI/2 = 335/226;
// EULER = 2721/1001; // 1.1e-7 // EULER = 2721/1001; // 1.1e-7
// EULER = 1264/465; // 2.2e-6 // EULER = 1264/465; // 2.2e-6
// get robust for small values. (effectively zero) // get robust for small values. (effectively zero)
if (abs(f) < 0.00001) if (abs(f) < 0.00001)
{ {
n = 0; n = 0;
d = 1; d = 1;
return; return;
} }
if (int32_t(f) == f) if (int32_t(f) == f)
{ {
n = int32_t(f); n = int32_t(f);
d = 1; d = 1;
return; return;
} }
// Normalize to 0.0 ... 1.0 // Normalize to 0.0 ... 1.0
bool negative = f < 0; bool negative = f < 0;
if (negative) f = -f; if (negative) f = -f;
// TODO investigate different strategy: // TODO investigate different strategy:
// intpart = int32_t(f); // strip of the integer part. // intpart = int32_t(f); // strip of the integer part.
@ -57,29 +57,29 @@ void Fraction::split(float f)
// determine n, d // determine n, d
// n += intpart * d; // add integer part * denominator to fraction. // n += intpart * d; // add integer part * denominator to fraction.
bool reciproke = f > 1; bool reciproke = f > 1;
if (reciproke) f = 1/f; if (reciproke) f = 1/f;
fractionize(f); fractionize(f);
simplify(); simplify();
// denormalize // denormalize
if (reciproke) if (reciproke)
{ {
int32_t t = n; int32_t t = n;
n = d; n = d;
d = t; d = t;
} }
if (negative) if (negative)
{ {
n = -n; n = -n;
} }
} }
Fraction::Fraction(int32_t p, int32_t q) : n(p), d(q) 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 Fraction::printTo(Print& p) const
{ {
size_t s = 0; size_t s = 0;
// TODO split of sign first // TODO split of sign first
// //
// vs 22/7 => 3_1/7 // vs 22/7 => 3_1/7
// if (n >= d) // if (n >= d)
// { // {
// s += p.print(n/d, DEC); // s += p.print(n/d, DEC);
// s += p.print("_"); // s += p.print("_");
// } // }
// s += p.print(n%d, DEC); // s += p.print(n%d, DEC);
s += p.print(n, DEC); s += p.print(n, DEC);
s += p.print('/'); s += p.print('/');
s += p.print(d, DEC); s += p.print(d, DEC);
return s; return s;
}; };
@ -112,7 +112,7 @@ size_t Fraction::printTo(Print& p) const
// //
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 float &f) // bool Fraction::operator == (const float &f)
@ -124,31 +124,31 @@ bool Fraction::operator == (const Fraction &c)
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) 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) 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) 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) 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 - () Fraction Fraction::operator - ()
{ {
return Fraction(-n, d); return Fraction(-n, d);
} }
@ -168,111 +168,111 @@ Fraction Fraction::operator - ()
// //
Fraction Fraction::operator + (const Fraction &c) Fraction Fraction::operator + (const Fraction &c)
{ {
if (d == c.d) if (d == c.d)
{ {
return Fraction(n + c.n, d); return Fraction(n + c.n, d);
} }
return Fraction(n*c.d + c.n*d, d * c.d); return Fraction(n*c.d + c.n*d, d * c.d);
} }
Fraction Fraction::operator - (const Fraction &c) Fraction Fraction::operator - (const Fraction &c)
{ {
if (d == c.d) if (d == c.d)
{ {
return Fraction(n - c.n, d); return Fraction(n - c.n, d);
} }
return Fraction(n*c.d - c.n*d, d * c.d); return Fraction(n*c.d - c.n*d, d * c.d);
} }
Fraction Fraction::operator * (const Fraction &c) 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) Fraction Fraction::operator / (const Fraction &c)
{ {
// division by zero returns 0 // division by zero returns 0
return Fraction(n * c.d, d * c.n); return Fraction(n * c.d, d * c.n);
} }
Fraction& Fraction::operator += (const Fraction &c) Fraction& Fraction::operator += (const Fraction &c)
{ {
if (d == c.d) if (d == c.d)
{ {
n += c.n; n += c.n;
} }
else else
{ {
n = n * c.d + c.n * d; n = n * c.d + c.n * d;
d *= c.d; d *= c.d;
} }
simplify(); simplify();
return *this; return *this;
} }
Fraction& Fraction::operator -= (const Fraction &c) Fraction& Fraction::operator -= (const Fraction &c)
{ {
if (d == c.d) if (d == c.d)
{ {
n -= c.n; n -= c.n;
} }
else else
{ {
n = n * c.d - c.n * d; n = n * c.d - c.n * d;
d *= c.d; d *= c.d;
} }
simplify(); simplify();
return *this; return *this;
} }
Fraction& Fraction::operator *= (const Fraction &c) Fraction& Fraction::operator *= (const Fraction &c)
{ {
n *= c.n; n *= c.n;
d *= c.d; d *= c.d;
simplify(); simplify();
return *this; return *this;
} }
Fraction& Fraction::operator /= (const Fraction &c) Fraction& Fraction::operator /= (const Fraction &c)
{ {
// division by zero returns 0 // division by zero returns 0
n *= c.d; n *= c.d;
d *= c.n; d *= c.n;
simplify(); simplify();
return *this; return *this;
} }
double Fraction::toDouble() double Fraction::toDouble()
{ {
return double(n) / d; return double(n) / d;
} }
float Fraction::toFloat() float Fraction::toFloat()
{ {
return float(n) / d; return float(n) / d;
} }
// fraction is proper if abs(fraction) < 1 // fraction is proper if abs(fraction) < 1
bool Fraction::isProper() bool Fraction::isProper()
{ {
return abs(n) < abs(d); return abs(n) < abs(d);
} }
// visualize fraction as an angle in degrees // visualize fraction as an angle in degrees
float Fraction::toAngle() 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. // at least if within precision.
Fraction Fraction::mediant(const Fraction &a, const Fraction &b) 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. // at least if within precision.
Fraction Fraction::middle(const Fraction &a, const Fraction &b) 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); // sort of setDenominator(uint16_t den);
Fraction Fraction::setDenominator(const Fraction &a, uint16_t b) Fraction Fraction::setDenominator(const Fraction &a, uint16_t b)
{ {
int32_t n = round((a.n * b * 1.0) / a.d); int32_t n = round((a.n * b * 1.0) / a.d);
int32_t d = b; int32_t d = b;
return Fraction(n, d); 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) int32_t Fraction::gcd(int32_t a , int32_t b)
{ {
while ( a != 0 ) while ( a != 0 )
{ {
int32_t c = a; int32_t c = a;
a = b % a; a = b % a;
b = c; b = c;
} }
return b; return b;
} }
// not that simple ... // not that simple ...
void Fraction::simplify() void Fraction::simplify()
{ {
if (n == 0) if (n == 0)
{ {
d = 1; d = 1;
return; return;
} }
bool neg = (n < 0) != (d < 0); bool neg = (n < 0) != (d < 0);
int32_t p = abs(n); int32_t p = abs(n);
int32_t q = abs(d); int32_t q = abs(d);
int32_t x = gcd(p,q); 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; p = p / x;
q = q / x; q = q / x;
}
// denominator max 4 digits keeps mul and div simple n = (neg) ? -p : p;
// in preventing overflow d = q;
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;
} }
@ -394,50 +394,51 @@ void Fraction::simplify()
// showed errors for very small values around 0 // showed errors for very small values around 0
void Fraction::fractionize(float val) void Fraction::fractionize(float val)
{ {
// find nearest fraction // find nearest fraction
float Precision = 0.0000001; float Precision = 0.0000001;
Fraction low(0, 1); // "A" = 0/1 Fraction low(0, 1); // "A" = 0/1
Fraction high(1, 1); // "B" = 1/1 Fraction high(1, 1); // "B" = 1/1
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
{ {
float testLow = low.d * val - low.n; float testLow = low.d * val - low.n;
float testHigh = high.n - high.d * val; float testHigh = high.n - high.d * val;
if (testHigh < Precision * high.d) if (testHigh < Precision * high.d)
break; // high is answer break; // high is answer
if (testLow < Precision * low.d)
{ // low is answer if (testLow < Precision * low.d)
high = low; { // low is answer
break; 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;
}
} }
n = high.n; if (i & 1)
d = high.d; { // 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;
} }

View File

@ -2,15 +2,14 @@
// //
// FILE: fraction.h // FILE: fraction.h
// AUTHOR: Rob Tillaart // AUTHOR: Rob Tillaart
// VERSION: 0.1.15 // VERSION: 0.1.16
// PURPOSE: Arduino library to implement a Fraction data type // PURPOSE: Arduino library to implement a Fraction data type
// URL: https://github.com/RobTillaart/Fraction // URL: https://github.com/RobTillaart/Fraction
//
#include "Arduino.h" #include "Arduino.h"
#define FRACTION_LIB_VERSION (F("0.1.15")) #define FRACTION_LIB_VERSION (F("0.1.16"))
class Fraction: public Printable class Fraction: public Printable

View File

@ -15,8 +15,8 @@
"type": "git", "type": "git",
"url": "https://github.com/RobTillaart/Fraction.git" "url": "https://github.com/RobTillaart/Fraction.git"
}, },
"version": "0.1.15", "version": "0.1.16",
"frameworks": "arduino", "frameworks": "*",
"platforms": "*", "platforms": "*",
"headers": "fraction.h" "headers": "fraction.h"
} }

View File

@ -1,8 +1,8 @@
name=Fraction name=Fraction
version=0.1.15 version=0.1.16
author=Rob Tillaart <rob.tillaart@gmail.com> author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=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. paragraph=Nominator and denominator are limited to 4 digits. Experimental.
category=Data Processing category=Data Processing
url=https://github.com/RobTillaart/Fraction url=https://github.com/RobTillaart/Fraction

View File

@ -2,8 +2,11 @@
[![Arduino CI](https://github.com/RobTillaart/Fraction/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) [![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) [![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) [![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) [![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) [![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 # Fraction
@ -51,7 +54,7 @@ In short, use fractions with care otherwise your sketch might get broken ;)
#### Printable #### Printable
The Fraction library implements Printable, so one can do. The Fraction library implements the Printable interface, so one can do.
```cpp ```cpp
Fraction fr(PI); Fraction fr(PI);
@ -102,7 +105,6 @@ The library is reasonably tested. If problems arise please open an issue.
#### Should #### Should
- performance testing - performance testing
- investigate divide by zero errors - investigate divide by zero errors
- NAN in fraction? => 0/0 ? - NAN in fraction? => 0/0 ?
- INF in fraction? => 1/0 and -1/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 #### 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,