diff --git a/.github/workflows/arduino-lint.yml b/.github/workflows/arduino-lint.yml index 0ad60f4..aed264b 100644 --- a/.github/workflows/arduino-lint.yml +++ b/.github/workflows/arduino-lint.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: arduino/arduino-lint-action@v2 with: library-manager: update diff --git a/.github/workflows/arduino_test_runner.yml b/.github/workflows/arduino_test_runner.yml index 1897982..a2a5f07 100644 --- a/.github/workflows/arduino_test_runner.yml +++ b/.github/workflows/arduino_test_runner.yml @@ -6,9 +6,8 @@ jobs: runTest: runs-on: ubuntu-latest timeout-minutes: 20 - steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 diff --git a/.github/workflows/jsoncheck.yml b/.github/workflows/jsoncheck.yml index 8804e69..2c52dc7 100644 --- a/.github/workflows/jsoncheck.yml +++ b/.github/workflows/jsoncheck.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: json-syntax-check uses: limitusus/json-syntax-check@v2 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index ca6014d..44f79b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.4.0] - 2026-06-15 +- fix **sign()** for -0 (0x8000) +- update unit test +- update GitHub actions +- minor edits + +---- + ## [0.3.1] - 2025-10-13 - update GitHub actions - update examples diff --git a/LICENSE b/LICENSE index 5446735..f85ccba 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2015-2025 Rob Tillaart +Copyright (c) 2015-2026 Rob Tillaart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 8ec210e..52b3f1b 100644 --- a/README.md +++ b/README.md @@ -24,23 +24,25 @@ The primary usage of the float16 data type is to efficiently store and transport a floating point number. As it uses only 2 bytes where float and double have typical 4 and 8 bytes, gains can be made at the price of range and precision. -Note that float16 only has ~3 significant digits. +Note that a float16 only has ~3 significant digits. -To print a float16, one need to convert it with toFloat(), toDouble() or toString(decimals). +To print a float16, one need to convert it with toFloat(), toDouble() or toString(decimals). The latter allows concatenation and further conversion to an char array. In pre 0.3.0 version the Printable interface was implemented, but it has been removed as it caused excessive memory usage when declaring arrays of float16. +Feedback as always is welcome. + ### ARM alternative half-precision --https://en.wikipedia.org/wiki/Half-precision_floating-point_format#ARM_alternative_half-precision +- https://en.wikipedia.org/wiki/Half-precision_floating-point_format#ARM_alternative_half-precision -_ARM processors support (via a floating point control register bit) -an "alternative half-precision" format, which does away with the -special case for an exponent value of 31 (111112).[10] It is almost -identical to the IEEE format, but there is no encoding for infinity or NaNs; +_ARM processors support (via a floating point control register bit) +an "alternative half-precision" format, which does away with the +special case for an exponent value of 31 (111112).[10] It is almost +identical to the IEEE format, but there is no encoding for infinity or NaNs; instead, an exponent of 31 encodes normalized numbers in the range 65536 to 131008._ Implemented in https://github.com/RobTillaart/float16ext class. @@ -48,7 +50,7 @@ Implemented in https://github.com/RobTillaart/float16ext class. ### Difference with float16 and float16ext -The float16ext library has an extended range as it supports values from +- 65504 +The float16ext library has an extended range as it supports values from +- 65504 to +- 131008. The float16ext does not support INF, -INF and NAN. These values are mapped upon @@ -56,18 +58,23 @@ the largest positive, the largest negative and the largest positive number. The -0 and 0 values will both exist. - Although they share a lot of code float16 and float16ext should not be mixed. In the future these libraries might merge / derive one from the other. +### Breaking change 0.4.0 + +Version 0.4.0 has fixed sign() which gave incorrect results for 0 and -0. + + ### Breaking change 0.3.0 -Version 0.3.0 has a breaking change. The **Printable** interface is removed as +Version 0.3.0 has a breaking change. The **Printable** interface is removed as it causes larger than expected arrays of float 16 (See #16). On ESP8266 every float16 object was 8 bytes and on AVR it was 5 bytes instead of the expected 2 bytes. To support printing the class added two new conversion functions: + ```cpp f16.toFloat(); f16.toString(decimals); @@ -75,6 +82,7 @@ f16.toString(decimals); Serial.println(f16.toFloat(), 4); Serial.println(f16.toString(4)); ``` + This keeps printing relative easy. The footprint of the library is now smaller and one can now create compact array's @@ -85,15 +93,15 @@ of float16 elements using only 2 bytes per element. Version 0.2.0 has a breaking change as a conversion bug has been found. See for details in issue #10. -For some specific values the mantissa overflowed when the float 16 was +For some specific values the mantissa overflowed when the float 16 was assigned a value to. This overflow was not detected / corrected. -During the analysis of this bug it became clear that the sub-normal numbers +During the analysis of this bug it became clear that the sub-normal numbers were also not implemented correctly. This is fixed too in 0.2.0. There is still an issue with 0 versus -0 (sign gets lost in conversion). -**This makes all pre-0.2.0 version obsolete.** +**This makes all pre-0.2.0 versions obsolete.** ## Specifications @@ -108,6 +116,7 @@ There is still an issue with 0 versus -0 (sign gets lost in conversion). | minimum | ±5.96046 E−8 | smallest number. | | ±1.0009765625 | 1 + 2^−10 = smallest number larger than 1. | maximum | ±65504 | +| range | | 12 orders of magnitude | | | ± = ALT 0177 @@ -119,24 +128,25 @@ Source: https://en.wikipedia.org/wiki/Half-precision_floating-point_format ```cpp /* - SIGN EXP MANTISSA - 0 01111 0000000000 = 1 - 0 01111 0000000001 = 1 + 2−10 = 1.0009765625 (next smallest float after 1) - 1 10000 0000000000 = −2 + SIGN EXPONENT MANTISSA + 0 01111 0000000000 = 1 + 0 01111 0000000001 = 1 + 2−10 = 1.0009765625 (next smallest float after 1) + 1 10000 0000000000 = −2 - 0 11110 1111111111 = 65504 (max half precision) + 0 11110 1111111111 = 65504 (max float16) + 0 11111 1111111111 = 131008 (max float16ext) - 0 00001 0000000000 = 2−14 ≈ 6.10352 × 10−5 (minimum positive normal) - 0 00000 1111111111 = 2−14 - 2−24 ≈ 6.09756 × 10−5 (maximum subnormal) - 0 00000 0000000001 = 2−24 ≈ 5.96046 × 10−8 (minimum positive subnormal) + 0 00001 0000000000 = 2−14 ≈ 6.10352 × 10−5 (minimum positive normal) + 0 00000 1111111111 = 2−14 - 2−24 ≈ 6.09756 × 10−5 (maximum subnormal) + 0 00000 0000000001 = 2−24 ≈ 5.96046 × 10−8 (minimum positive subnormal) - 0 00000 0000000000 = 0 - 1 00000 0000000000 = −0 + 0 00000 0000000000 = 0 + 1 00000 0000000000 = −0 - 0 11111 0000000000 = infinity - 1 11111 0000000000 = −infinity + 0 11111 0000000000 = infinity + 1 11111 0000000000 = −infinity - 0 01101 0101010101 = 0.333251953125 ≈ 1/3 + 0 01101 0101010101 = 0.333251953125 ≈ 1/3 */ ``` @@ -147,6 +157,7 @@ Source: https://en.wikipedia.org/wiki/Half-precision_floating-point_format - https://github.com/RobTillaart/float16 - https://github.com/RobTillaart/float16ext - https://github.com/RobTillaart/fraction +- https://github.com/RobTillaart/printHelpers - scientific format a.o. - https://en.wikipedia.org/wiki/Half-precision_floating-point_format @@ -163,7 +174,7 @@ Source: https://en.wikipedia.org/wiki/Half-precision_floating-point_format - **float16(const float16 &f)** copy constructor. -### Conversion +### Conversion / printing - **double toDouble(void)** convert value to double or float (if the same e.g. UNO). - **float toFloat(void)** convert value to float. @@ -182,7 +193,7 @@ To serialize the internal format e.g. to disk, two helper functions are availabl ### Compare -The library implement the standard compare functions. +The library implement the standard compare functions. These are optimized, so it is fast to compare 2 float16 values. Note: comparison with a float or double always include a conversion. @@ -217,7 +228,7 @@ Negation operator. Math helpers. - **int sign()** returns 1 == positive, 0 == zero, -1 == negative. - **bool isZero()** returns true if zero. slightly faster than **sign()**. -- **bool isNaN()** returns true if value is not a number. +- **bool isNaN()** returns true if value is not a number. - **bool isInf()** returns true if value is ± infinite. - **bool isPosInf()** returns true if value is + infinite. - **bool isNegInf()** returns true if value is - infinite. @@ -231,7 +242,8 @@ Math helpers. #### Should -- how to handle 0 == -0 (0x0000 == 0x8000) +- how to handle 0 == -0 (0x0000 == 0x8000) or (0x8000 == 0x0000) + - if ((_value & 0x7FFF) == 0x0000) return (f._value & 0x7FFF) == 0x0000; #### Could diff --git a/examples/float16_sizeof_array/float16_sizeof_array.ino b/examples/float16_sizeof_array/float16_sizeof_array.ino index 99f3f3e..19bb112 100644 --- a/examples/float16_sizeof_array/float16_sizeof_array.ino +++ b/examples/float16_sizeof_array/float16_sizeof_array.ino @@ -3,7 +3,7 @@ // AUTHOR: Rob Tillaart // PURPOSE: test float16 size // URL: https://github.com/RobTillaart/float16 -// See #12 +// See issue #12 #include "Arduino.h" #include "float16.h" @@ -35,7 +35,7 @@ void setup() Serial.println(sizeof(test32[0])); Serial.println(); - // set some values to make sure the compiler doesn't optimise out the arrays. + // set some values to make sure the compiler doesn't optimise out the arrays. test16[5] = 32; test32[4] = 32; diff --git a/examples/float16_test0/float16_test0.ino b/examples/float16_test0/float16_test0.ino index 374ae40..107c53d 100644 --- a/examples/float16_test0/float16_test0.ino +++ b/examples/float16_test0/float16_test0.ino @@ -42,6 +42,20 @@ void setup() Serial.println("Start "); + // // sign test + // float16 a(0); + // Serial.println(a.sign()); + // Serial.println(a.getBinary(), HEX); + // a.setBinary(0x8000); + // Serial.println(a.sign()); + // a.setBinary(0x0000); + // Serial.println(a.sign()); + // a.setBinary(0x8100); + // Serial.println(a.sign()); + // a.setBinary(0x0100); + // Serial.println(a.sign()); + // while (1); + Serial.println(); test_constructors(); @@ -133,7 +147,7 @@ void test_numbers() float16 c(1 / 2000.0); Serial.println(c.toDouble(), 9); - c = 1/3.0; + c = 1 / 3.0; Serial.println(c.toDouble(), 9); float16 d(1); diff --git a/float16.cpp b/float16.cpp index 1aaa0bb..1bd8b9d 100644 --- a/float16.cpp +++ b/float16.cpp @@ -1,7 +1,7 @@ // // FILE: float16.cpp // AUTHOR: Rob Tillaart -// VERSION: 0.3.1 +// VERSION: 0.4.0 // DATE: 2015-03-10 // PURPOSE: library for Float16s for Arduino // URL: http://en.wikipedia.org/wiki/Half-precision_floating-point_format @@ -43,6 +43,7 @@ String float16::toString(unsigned int decimals) const // bool float16::operator == (const float16 &f) { + // if ((_value & 0x7FFF) == 0x0000) return (f._value & 0x7FFF) == 0x0000; return (_value == f._value); } @@ -151,9 +152,12 @@ float16& float16::operator /= (const float16 &f) // int float16::sign() { - if (_value & 0x8000) return -1; - if (_value & 0xFFFF) return 1; - return 0; + // zero test matches 0x8000 too + if ((_value & 0x7FFF) == 0x0000) return 0; + // positive test including positive infinity + if (_value <= 0x7FFF) return 1; + // remaining is negative + return -1; } bool float16::isZero() @@ -225,7 +229,7 @@ float float16::f16tof32(uint16_t _value) const uint16_t float16::f32tof16(float f) const { - uint32_t t = *(uint32_t *) &f; + uint32_t t = *((uint32_t *) &f); // man bits = 10; but we keep 11 for rounding uint16_t man = (t & 0x007FFFFF) >> 12; int16_t exp = (t & 0x7F800000) >> 23; diff --git a/float16.h b/float16.h index 77b205d..43fb63d 100644 --- a/float16.h +++ b/float16.h @@ -2,7 +2,7 @@ // // FILE: float16.h // AUTHOR: Rob Tillaart -// VERSION: 0.3.1 +// VERSION: 0.4.0 // DATE: 2015-03-10 // PURPOSE: Arduino library to implement float16 data type. // half-precision floating point format, @@ -12,7 +12,7 @@ #include "Arduino.h" -#define FLOAT16_LIB_VERSION (F("0.3.1")) +#define FLOAT16_LIB_VERSION (F("0.4.0")) class float16 diff --git a/library.json b/library.json index 631261e..c1adff0 100644 --- a/library.json +++ b/library.json @@ -15,7 +15,7 @@ "type": "git", "url": "https://github.com/RobTillaart/float16.git" }, - "version": "0.3.1", + "version": "0.4.0", "license": "MIT", "frameworks": "*", "platforms": "*", diff --git a/library.properties b/library.properties index 88b84a9..612d72d 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=float16 -version=0.3.1 +version=0.4.0 author=Rob Tillaart maintainer=Rob Tillaart sentence=Arduino library to implement float16 data type. diff --git a/test/unit_test_001.cpp b/test/unit_test_001.cpp index 4484c89..743a02e 100644 --- a/test/unit_test_001.cpp +++ b/test/unit_test_001.cpp @@ -95,6 +95,28 @@ unittest(test_sizeof) } +unittest(test_sign) +{ + float16 a(0); + assertEqual( 0, a.sign()); + + a.setBinary(0x0000); // +0 + assertEqual( 0, a.sign()); + a.setBinary(0x8000); // -0 + assertEqual( 0, a.sign()); + + a.setBinary(0x0100); + assertEqual( 1, a.sign()); + a.setBinary(0x8100); + assertEqual(-1, a.sign()); + + a.setBinary(0x7C00); // positive infinity + assertEqual( 1, a.sign()); + a.setBinary(0xFC00); // negative infinity + assertEqual(-1, a.sign()); +} + + unittest(test_compare_equal) { float16 a(1);