0.2.2 IEEE754tools

This commit is contained in:
rob tillaart 2021-11-05 17:24:10 +01:00
parent 8d8eda436c
commit 87fd0665ec
10 changed files with 369 additions and 67 deletions

View File

@ -2,6 +2,10 @@ compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
- uno
- leonardo
- due
- zero
# - due
# - zero
# - leonardo
- m4
- esp32
# - esp8266
# - mega2560

View File

@ -4,10 +4,14 @@ name: Arduino CI
on: [push, pull_request]
jobs:
arduino_ci:
runTest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Arduino-CI/action@master
# Arduino-CI/action@v0.1.1
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
- run: |
gem install arduino_ci
arduino_ci.rb

View File

@ -2,7 +2,7 @@
//
// FILE: IEEE754tools.h
// AUTHOR: Rob Tillaart
// VERSION: 0.2.1
// VERSION: 0.2.2
// PURPOSE: manipulate IEEE754 float numbers fast
// URL: https://github.com/RobTillaart/IEEE754tools.git
//
@ -15,10 +15,14 @@
// 0.1.03 2013-09-10 renamed IEEE_Sign IEEE_Exponent
// 0.2.0 2020-06-30 own repo + some refactor...
// 0.2.1 2020-12-30 arduino-CI
// 0.2.2 2021-11-05 update Arduino-CI, badges, readme.md
// some testing on ESP32 (no fixing)
#include "Arduino.h"
#define IEEE754_VERSION "0.2.1"
#define IEEE754_VERSION (F("0.2.2"))
// (un)comment lines to configure functionality / size
//#define IEEE754_ENABLE_MSB // +78 bytes
@ -32,6 +36,7 @@ struct IEEEfloat
uint8_t s:1;
};
// IEEE754 double layout;
struct IEEEdouble
{
@ -40,6 +45,7 @@ struct IEEEdouble
uint8_t s:1;
};
// Arduino UNO double layout:
// the UNO has no 64 bit double, it is only able to map 23 bits of the mantisse
// a filler is added for the remaining bits. These might be useful in future?
@ -51,6 +57,7 @@ struct _DBL
uint8_t s:1;
};
// for packing and unpacking a float
union _FLOATCONV
{
@ -59,6 +66,7 @@ union _FLOATCONV
byte b[4];
};
// for packing and unpacking a double
union _DBLCONV
{
@ -87,6 +95,7 @@ void dumpFloat(float number)
// Serial.print(" mant: "); Serial.println(x->m);
}
// print "double" components
void dumpDBL(struct _DBL dbl)
{
@ -97,12 +106,13 @@ void dumpDBL(struct _DBL dbl)
Serial.println(dbl.m, HEX);
}
//
// mapping to/from 64bit double - best effort
//
// converts a float to a packed array of 8 bytes representing a 64 bit double
// restriction exponent and mantisse.
// restriction exponent and mantissa.
// float; array of 8 bytes; LSBFIRST; MSBFIRST
void float2DoublePacked(float number, byte* bar, int byteOrder = LSBFIRST)
{
@ -134,9 +144,10 @@ void float2DoublePacked(float number, byte* bar, int byteOrder = LSBFIRST)
#endif
}
// converts a packed array of bytes into a 32bit float.
// there can be an exponent overflow
// the mantisse is truncated to 23 bits.
// the mantissa is truncated to 23 bits.
float doublePacked2Float(byte* bar, int byteOrder = LSBFIRST)
{
_FLOATCONV fl;
@ -174,6 +185,7 @@ float doublePacked2Float(byte* bar, int byteOrder = LSBFIRST)
// return (fl.p.s) ? -INFINITY : INFINITY;
}
//
// TEST FUNCTIONS
//
@ -185,6 +197,7 @@ int IEEE_NAN(float number)
return ((*x) == 0x7FC0);
}
// ~3.4x faster
int IEEE_INF(float number)
{
@ -195,7 +208,8 @@ int IEEE_INF(float number)
return 0;
}
// for the real speed freaks, the next two
// for the real speed freaks, the next
boolean IEEE_PosINF(float number)
{
return (* ((uint16_t*) &number + 1) ) == 0x7F80;
@ -207,8 +221,6 @@ boolean IEEE_NegINF(float number)
}
//
// PROPERTIES
//
@ -230,10 +242,12 @@ uint32_t IEEE_Mantisse(float number)
return x->m;
}
//
// MATH FUNCTIONS
//
// f = f * 2^n
// factor ~2.7; (tested with *16) more correct than the faster one
float IEEE_POW2(float number, int n)
{
@ -248,6 +262,7 @@ float IEEE_POW2(float number, int n)
return (fl.p.s) ? -INFINITY : INFINITY;
}
// WARNING no overflow detection in the SHIFT (factor ~3.5)
float IEEE_POW2fast(float number, int n)
{
@ -258,7 +273,45 @@ float IEEE_POW2fast(float number, int n)
}
// - FAILS ON ESP32 (x16 => x256 strange)
float IEEE_FLOAT_POW2fast(float number, int n)
{
IEEEfloat* x = (IEEEfloat*) ((void*)&number);
x->e += n;
return number;
}
// - NOT FASTER
// - FAILS ON ESP32 (==> divides by 4)
float IEEE_FLOAT_DIV2(float number)
{
IEEEfloat* x = (IEEEfloat*) ((void*)&number);
x->e--;
return number;
}
bool IEEE_FLOAT_EQ(float &f, float &g)
{
uint16_t *p = (uint16_t *) &f;
uint16_t *q = (uint16_t *) &g;
return (*p++ == *q++) && (*p++ == *q++);
}
bool IEEE_FLOAT_NEQ(float &f, float &g)
{
uint16_t *p = (uint16_t *) &f;
uint16_t *q = (uint16_t *) &g;
return (*p++ != *q++) || (*p++ != *q++);
}
////////////////////////////////////////////////////////////////////////////////
//
// NOT TESTED FUNCTIONS
//
@ -328,14 +381,17 @@ float IEEE_FLIP(float number)
// x = *(((byte*) &number)+3) & 0x7F; // x = fabs(number);
// GAIN = factor 2
// *(((byte*) &number)+3) |= 0x80; // number == -fabs(number);
// x = *(((byte*) &number)+3) | 0x80; // x == -fabs(number);
// GAIN = factor 2
// *(((byte*) &number)+3) ^= 0x80; // number = -number;
// x = *(((byte*) &number)+3) ^ 0x80; // x = -number;
// GAIN = factor 2
// s = *(((uint8_t*) &number)+3) & 0x80; // s = sign(number);
// if ( *(((byte*) &number)+3) & 0x80) x=2; // if (number < 0) x=2;
// GAIN = factor 5
@ -348,12 +404,6 @@ boolean IEEE_ZERO(float number)
return (* ((uint32_t*) &number) ) & 0x7FFFFFFF;
}
float IEEE_DIV2(float number)
{
IEEEfloat* x = (IEEEfloat*) ((void*)&number);
x->e--;
return number;
}
bool IEEE_LESS(float f, float g)
{
@ -367,13 +417,16 @@ bool IEEE_LESS(float f, float g)
return 0;
}
bool IEEE_EQ(float f, float g)
bool IEEE_FLOAT_EQ(float &f, float &g)
{
IEEEfloat* x = (IEEEfloat*) ((void*)&f);
IEEEfloat* y = (IEEEfloat*) ((void*)&g);
return (x->m == y->m) && (x->e == y->e) && (x->s != y->s);
not fast enough
return (memcmp(&f, &g, 4) == 0);
return (* ((uint32_t *) &f) - * ((uint32_t *) &g)) == 0 ;
}
*/
// -- END OF FILE --

View File

@ -31,14 +31,16 @@ void setup()
void test_negfabs()
{
Serial.println(__FUNCTION__);
Serial.println("zz = -fabs(zz)");
Serial.println("TEST : zz = -fabs(zz)");
start = micros();
for (int i = 0; i < 30000; i++)
{
*(((byte*) &zz) + 3) |= 0x80; // Force negative == -fabs(zz);
}
duration1 = micros() - start;
Serial.println(duration1 / 30000.0);
Serial.print("TIME : ");
Serial.println(duration1 / 30000.0, 4);
Serial.print("VALUE: ");
Serial.println(zz);
delay(10);
@ -49,10 +51,12 @@ void test_negfabs()
zz = -fabs(zz);
}
duration2 = micros() - start;
Serial.println(duration2 / 30000.0);
Serial.print("Gain:\t");
Serial.println(1.0 * duration2 / duration1);
Serial.print("TIME : ");
Serial.println(duration2 / 30000.0, 4);
Serial.print("VALUE: ");
Serial.println(zz);
Serial.print("GAIN : ");
Serial.println(1.0 * duration2 / duration1, 2);
Serial.println();
delay(10);
}
@ -60,14 +64,16 @@ void test_negfabs()
void test_fabs()
{
Serial.println(__FUNCTION__);
Serial.println("zz = fabs(zz)");
Serial.println("TEST : zz = fabs(zz)");
start = micros();
for (int i = 0; i < 30000; i++)
{
*(((byte*) &zz) + 3) &= 0x7F; // force positive == fabs(zz);
}
duration1 = micros() - start;
Serial.println(duration1 / 30000.0);
Serial.print("TIME : ");
Serial.println(duration1 / 30000.0, 4);
Serial.print("VALUE: ");
Serial.println(zz);
delay(10);
@ -77,10 +83,12 @@ void test_fabs()
zz = fabs(zz);
}
duration2 = micros() - start;
Serial.println(duration2 / 30000.0);
Serial.print("Gain:\t");
Serial.println(1.0 * duration2 / duration1);
Serial.print("TIME : ");
Serial.println(duration2 / 30000.0, 4);
Serial.print("VALUE: ");
Serial.println(zz);
Serial.print("GAIN : ");
Serial.println(1.0 * duration2 / duration1, 2);
Serial.println();
delay(10);
}
@ -88,14 +96,16 @@ void test_fabs()
void test_negate()
{
Serial.println(__FUNCTION__);
Serial.println("zz = -zz");
Serial.println("TEST : zz = -zz");
start = micros();
for (int i = 0; i < 30000; i++)
{
*(((byte*) &zz) + 3) ^= 0x80;
}
duration1 = micros() - start;
Serial.println(duration1 / 30000.0);
Serial.print("TIME : ");
Serial.println(duration1 / 30000.0, 4);
Serial.print("VALUE: ");
Serial.println(zz);
start = micros();
@ -104,10 +114,12 @@ void test_negate()
zz = -zz;
}
duration2 = micros() - start;
Serial.println(duration2 / 30000.0);
Serial.print("Gain:\t");
Serial.println(1.0 * duration2 / duration1);
Serial.print("TIME : ");
Serial.println(duration2 / 30000.0, 4);
Serial.print("VALUE: ");
Serial.println(zz);
Serial.print("GAIN : ");
Serial.println(1.0 * duration2 / duration1, 2);
Serial.println();
delay(10);
}
@ -115,14 +127,16 @@ void test_negate()
void test_less_zero()
{
Serial.println(__FUNCTION__);
Serial.println("if (zz < 0) ");
Serial.println("TEST : if (zz < 0) ");
start = micros();
for (int i = 0; i < 30000; i++)
{
if ( *(((byte*) &zz) + 3) & 0x80) x = 2; // equals if (zz < 0);
}
duration1 = micros() - start;
Serial.println(duration1 / 30000.0);
Serial.print("TIME : ");
Serial.println(duration1 / 30000.0, 4);
Serial.print("VALUE: ");
Serial.println(zz);
start = micros();
@ -131,18 +145,20 @@ void test_less_zero()
if (zz < 0) x = 2;
}
duration2 = micros() - start;
Serial.println(duration2 / 30000.0);
Serial.print("Gain:\t");
Serial.println(1.0 * duration2 / duration1);
Serial.print("TIME : ");
Serial.println(duration2 / 30000.0, 4);
Serial.print("VALUE: ");
Serial.println(zz);
Serial.print("GAIN : ");
Serial.println(1.0 * duration2 / duration1, 2);
start = micros();
for (int i = 0; i < 30000; i++)
{
x = 2;
}
Serial.print("TIME : ");
Serial.println((micros() - start) / 30000.0, 4);
Serial.println(zz);
Serial.println();
delay(10);
}

View File

@ -0,0 +1,194 @@
//
// FILE: //
// FILE: IEEE754_equal.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: experimental
//
// NOTE
// - WORKS ON AVR
// - FAILS FOR ESP32
#include <IEEE754tools.h>
uint32_t start, duration;
volatile bool b;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
test_FLOAT_EQ_1();
test_FLOAT_EQ_2();
test_DIV2();
test_POW2();
Serial.println("done");
}
void loop()
{
}
void test_FLOAT_EQ_1()
{
Serial.println();
Serial.println(__FUNCTION__);
float f = PI;
float g = PI;
Serial.print("IEEE_FLOAT_EQ : ");
Serial.println(IEEE_FLOAT_EQ(f, g));
Serial.print("IEEE_FLOAT_NEQ: ");
Serial.println(IEEE_FLOAT_NEQ(f, g));
f += 1;
Serial.print("IEEE_FLOAT_EQ : ");
Serial.println(IEEE_FLOAT_EQ(f, g));
Serial.print("IEEE_FLOAT_NEQ: ");
Serial.println(IEEE_FLOAT_NEQ(f, g));
Serial.println();
}
void test_FLOAT_EQ_2()
{
Serial.println();
Serial.println(__FUNCTION__);
delay(10);
float f = PI;
float g = PI;
start = micros();
b = (f == g);
duration = micros() - start;
Serial.print("TIME : ");
Serial.println(duration);
delay(10);
start = micros();
b = IEEE_FLOAT_EQ(f, g);
duration = micros() - start;
Serial.print("TIME : ");
Serial.println(duration);
delay(10);
g += 1;
start = micros();
b = (f == g);
duration = micros() - start;
Serial.print("TIME : ");
Serial.println(duration);
delay(10);
start = micros();
b = IEEE_FLOAT_EQ(f, g);
duration = micros() - start;
Serial.print("TIME : ");
Serial.println(duration);
Serial.println();
}
void test_DIV2()
{
Serial.println();
Serial.println(__FUNCTION__);
delay(10);
volatile float f = random(1000) * 0.01;
Serial.print("VALUE: ");
Serial.println(f, 10);
delay(10);
start = micros();
float g = f / 2;
duration = micros() - start;
Serial.print("TIME : ");
Serial.println(duration);
Serial.print("VALUE: ");
Serial.println(f, 10);
Serial.print("VALUE: ");
Serial.println(g, 10);
delay(10);
start = micros();
g = IEEE_FLOAT_DIV2(f);
duration = micros() - start;
Serial.print("TIME : ");
Serial.println(duration);
Serial.print("VALUE: ");
Serial.println(f, 10);
Serial.print("VALUE: ");
Serial.println(g, 10);
delay(10);
start = micros();
g = IEEE_FLOAT_POW2fast(f, -1);
duration = micros() - start;
Serial.print("TIME : ");
Serial.println(duration);
Serial.print("VALUE: ");
Serial.println(f, 10);
Serial.print("VALUE: ");
Serial.println(g, 10);
delay(10);
Serial.println();
}
void test_POW2()
{
Serial.println();
Serial.println(__FUNCTION__);
delay(10);
int p = random(10);
float f = random(1000) * 0.001;
Serial.println(f, 10);
delay(10);
start = micros();
float g = f * pow(2, p);
duration = micros() - start;
Serial.print("TIME : ");
Serial.println(duration);
Serial.print("VALUE: ");
Serial.println(g, 10);
delay(10);
start = micros();
g = IEEE_POW2(f, p);
duration = micros() - start;
Serial.print("TIME : ");
Serial.println(duration);
Serial.print("VALUE: ");
Serial.println(g, 10);
delay(10);
start = micros();
g = IEEE_FLOAT_POW2fast(f, p);
duration = micros() - start;
Serial.print("TIME : ");
Serial.println(duration);
Serial.print("VALUE: ");
Serial.println(g, 10);
delay(10);
Serial.println();
}
// -- END OF FILE --

View File

@ -5,6 +5,11 @@
// PURPOSE: experimental expands a float in a IEEE 754 double to be printed to PC.
//
// NOTE
// - WORKS ON AVR
// - FAILS ON ESP32
#include <IEEE754tools.h>
byte x[8];
@ -24,6 +29,7 @@ void setup()
void test1()
{
Serial.println();
Serial.println(__FUNCTION__);
for (float f = -50.0; f < 50.0; f += 10.0)
{
dumpFloat(f);
@ -38,6 +44,8 @@ void test1()
void test2()
{
Serial.println();
Serial.println(__FUNCTION__);
Serial.println("\n0.15625");
dumpFloat(0.15625);
// sign = 0
@ -47,7 +55,9 @@ void test2()
void test3()
{
Serial.println("\nPI-check");
Serial.println();
Serial.println(__FUNCTION__);
Serial.println("PI-check");
Serial.println(PI, 20);
float2DoublePacked(PI, x);
dumpByteArray(x);
@ -58,7 +68,9 @@ void test3()
void test4()
{
Serial.println("\nBIG-check");
Serial.println();
Serial.println(__FUNCTION__);
Serial.println("BIG-check");
Serial.println(1.23456789e38, 20);
dumpFloat(1.23456789e38);
float2DoublePacked(1.23456789e38, x);
@ -74,20 +86,16 @@ void loop()
{
}
void dumpByteArray(byte *ar)
{
for (int i = 0; i < 8; i++)
{
if (ar[i] < 0x10) Serial.print('0');
Serial.print(ar[i], HEX);
Serial.print('\t');
Serial.print(' ');
}
Serial.println();
}
// -- END OF FILE --

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/IEEE754tools.git"
},
"version": "0.2.1",
"version": "0.2.2",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*"

View File

@ -1,5 +1,5 @@
name=IEEE754tools
version=0.2.1
version=0.2.2
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Fast helper functions for IEEE754 floats.

View File

@ -1,5 +1,7 @@
[![Arduino CI](https://github.com/RobTillaart/IEEE754tools/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![Arduino-lint](https://github.com/RobTillaart/IEEE754tools/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/IEEE754tools/actions/workflows/arduino-lint.yml)
[![JSON check](https://github.com/RobTillaart/IEEE754tools/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/IEEE754tools/actions/workflows/jsoncheck.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/IEEE754tools/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/IEEE754tools.svg?maxAge=3600)](https://github.com/RobTillaart/IEEE754tools/releases)
@ -8,11 +10,12 @@
Arduino library to manipulate IEEE754 float numbers fast.
## Description
IEEE754tools.h contains a collection of bit-hacks to speed up a number
of operations on floating point numbers on the Arduino. These bit-hacks
started in 2010 (oldest code found) or maybe even earlier.
IEEE754tools.h contains a collection of experimental bit-hacks to speed up
a number of operations on floating point numbers on the **Arduino UNO**.
These bit-hacks started in 2010 (oldest code found), maybe even earlier.
http://en.wikipedia.org/wiki/Double_precision
@ -21,17 +24,37 @@ http://en.wikipedia.org/wiki/Single-precision_floating-point_format
## WARNING
- **do not use** this lib for production code unless you verified the correctness.
Code is experimental, so use with care at your own risk.
- If you don't need micro-second speed ups **do not use** these code snippets.
- code is experimental, so use with care.
- only tested on UNO
- **do not use for ESP32** ESP32 does things differently ==> not all code works.
## Test results examples
updated for 0.2.2
| example | UNO | ESP32 |
|:--------------|:------:|:-------:|
| fastNegate | Y | Y |
| float2Double | Y | N |
| IEEE754_equal | Y | N |
ESP32 - needs investigation as UNO verified code fails.
(something for a long winter)
## Future
- test with double
- test on ESP23
- investigate other speed ups
- write more examples
- test on ESP32
- test with double
- binary transfer over Serial
- example binary transfer over Serial
- example binary transfer over Ethernet
- bring more structure in this library.
## Operations

View File

@ -60,7 +60,7 @@ unittest(test_new_operator)
unittest(test_all)
{
fprintf(stderr, "VERSION: %s\n", IEEE754_VERSION);
fprintf(stderr, "VERSION: %s\n", (char*) IEEE754_VERSION);
fprintf(stderr, "Convert PI to double and back\n");
uint8_t ar[8];