summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.pre-commit-config.yaml3
-rw-r--r--core/io/file_access.cpp10
-rw-r--r--core/io/file_access.h2
-rw-r--r--core/io/marshalls.h10
-rw-r--r--core/io/stream_peer.cpp26
-rw-r--r--core/io/stream_peer.h2
-rw-r--r--doc/classes/FileAccess.xml13
-rw-r--r--doc/classes/StreamPeer.xml13
-rw-r--r--tests/core/io/test_file_access.h94
-rw-r--r--tests/core/io/test_marshalls.h21
-rw-r--r--tests/core/io/test_stream_peer.h22
-rw-r--r--tests/data/floating_point_big_endian.bin1
-rw-r--r--tests/data/floating_point_little_endian.bin1
-rw-r--r--tests/data/half_precision_floating_point_big_endian.bin1
-rw-r--r--tests/data/half_precision_floating_point_little_endian.bin1
15 files changed, 218 insertions, 2 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d81c1043a7..a0eba86244 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -187,7 +187,8 @@ repos:
modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment\.notest\.gd$|
modules/gdscript/tests/scripts/parser/warnings/empty_file_newline\.notest\.gd$|
platform/android/java/editor/src/main/java/com/android/.*|
- platform/android/java/lib/src/com/google/.*
+ platform/android/java/lib/src/com/google/.*|
+ tests/data/.*\.bin$
)
- id: dotnet-format
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp
index d8bf645a7d..01b189f5f2 100644
--- a/core/io/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -263,6 +263,10 @@ uint64_t FileAccess::get_64() const {
return data;
}
+float FileAccess::get_half() const {
+ return Math::half_to_float(get_16());
+}
+
float FileAccess::get_float() const {
MarshallFloat m;
m.i = get_32();
@@ -522,6 +526,10 @@ void FileAccess::store_real(real_t p_real) {
}
}
+void FileAccess::store_half(float p_dest) {
+ store_16(Math::make_half_float(p_dest));
+}
+
void FileAccess::store_float(float p_dest) {
MarshallFloat m;
m.f = p_dest;
@@ -828,6 +836,7 @@ void FileAccess::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_16"), &FileAccess::get_16);
ClassDB::bind_method(D_METHOD("get_32"), &FileAccess::get_32);
ClassDB::bind_method(D_METHOD("get_64"), &FileAccess::get_64);
+ ClassDB::bind_method(D_METHOD("get_half"), &FileAccess::get_half);
ClassDB::bind_method(D_METHOD("get_float"), &FileAccess::get_float);
ClassDB::bind_method(D_METHOD("get_double"), &FileAccess::get_double);
ClassDB::bind_method(D_METHOD("get_real"), &FileAccess::get_real);
@@ -846,6 +855,7 @@ void FileAccess::_bind_methods() {
ClassDB::bind_method(D_METHOD("store_16", "value"), &FileAccess::store_16);
ClassDB::bind_method(D_METHOD("store_32", "value"), &FileAccess::store_32);
ClassDB::bind_method(D_METHOD("store_64", "value"), &FileAccess::store_64);
+ ClassDB::bind_method(D_METHOD("store_half", "value"), &FileAccess::store_half);
ClassDB::bind_method(D_METHOD("store_float", "value"), &FileAccess::store_float);
ClassDB::bind_method(D_METHOD("store_double", "value"), &FileAccess::store_double);
ClassDB::bind_method(D_METHOD("store_real", "value"), &FileAccess::store_real);
diff --git a/core/io/file_access.h b/core/io/file_access.h
index 7f5687fe03..de23edf5b5 100644
--- a/core/io/file_access.h
+++ b/core/io/file_access.h
@@ -142,6 +142,7 @@ public:
virtual uint32_t get_32() const; ///< get 32 bits uint
virtual uint64_t get_64() const; ///< get 64 bits uint
+ virtual float get_half() const;
virtual float get_float() const;
virtual double get_double() const;
virtual real_t get_real() const;
@@ -173,6 +174,7 @@ public:
virtual void store_32(uint32_t p_dest); ///< store 32 bits uint
virtual void store_64(uint64_t p_dest); ///< store 64 bits uint
+ virtual void store_half(float p_dest);
virtual void store_float(float p_dest);
virtual void store_double(double p_dest);
virtual void store_real(real_t p_real);
diff --git a/core/io/marshalls.h b/core/io/marshalls.h
index 6f015ac386..82c760c28d 100644
--- a/core/io/marshalls.h
+++ b/core/io/marshalls.h
@@ -84,6 +84,12 @@ static inline unsigned int encode_uint32(uint32_t p_uint, uint8_t *p_arr) {
return sizeof(uint32_t);
}
+static inline unsigned int encode_half(float p_float, uint8_t *p_arr) {
+ encode_uint16(Math::make_half_float(p_float), p_arr);
+
+ return sizeof(uint16_t);
+}
+
static inline unsigned int encode_float(float p_float, uint8_t *p_arr) {
MarshallFloat mf;
mf.f = p_float;
@@ -172,6 +178,10 @@ static inline uint32_t decode_uint32(const uint8_t *p_arr) {
return u;
}
+static inline float decode_half(const uint8_t *p_arr) {
+ return Math::half_to_float(decode_uint16(p_arr));
+}
+
static inline float decode_float(const uint8_t *p_arr) {
MarshallFloat mf;
mf.i = decode_uint32(p_arr);
diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp
index 3f1c468fb3..045904fb5d 100644
--- a/core/io/stream_peer.cpp
+++ b/core/io/stream_peer.cpp
@@ -178,6 +178,18 @@ void StreamPeer::put_64(int64_t p_val) {
put_data(buf, 8);
}
+void StreamPeer::put_half(float p_val) {
+ uint8_t buf[2];
+
+ encode_half(p_val, buf);
+ uint16_t *p16 = (uint16_t *)buf;
+ if (big_endian) {
+ *p16 = BSWAP16(*p16);
+ }
+
+ put_data(buf, 2);
+}
+
void StreamPeer::put_float(float p_val) {
uint8_t buf[4];
@@ -294,6 +306,18 @@ int64_t StreamPeer::get_64() {
return r;
}
+float StreamPeer::get_half() {
+ uint8_t buf[2];
+ get_data(buf, 2);
+
+ uint16_t *p16 = (uint16_t *)buf;
+ if (big_endian) {
+ *p16 = BSWAP16(*p16);
+ }
+
+ return decode_half(buf);
+}
+
float StreamPeer::get_float() {
uint8_t buf[4];
get_data(buf, 4);
@@ -385,6 +409,7 @@ void StreamPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("put_u32", "value"), &StreamPeer::put_u32);
ClassDB::bind_method(D_METHOD("put_64", "value"), &StreamPeer::put_64);
ClassDB::bind_method(D_METHOD("put_u64", "value"), &StreamPeer::put_u64);
+ ClassDB::bind_method(D_METHOD("put_half", "value"), &StreamPeer::put_half);
ClassDB::bind_method(D_METHOD("put_float", "value"), &StreamPeer::put_float);
ClassDB::bind_method(D_METHOD("put_double", "value"), &StreamPeer::put_double);
ClassDB::bind_method(D_METHOD("put_string", "value"), &StreamPeer::put_string);
@@ -399,6 +424,7 @@ void StreamPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_u32"), &StreamPeer::get_u32);
ClassDB::bind_method(D_METHOD("get_64"), &StreamPeer::get_64);
ClassDB::bind_method(D_METHOD("get_u64"), &StreamPeer::get_u64);
+ ClassDB::bind_method(D_METHOD("get_half"), &StreamPeer::get_half);
ClassDB::bind_method(D_METHOD("get_float"), &StreamPeer::get_float);
ClassDB::bind_method(D_METHOD("get_double"), &StreamPeer::get_double);
ClassDB::bind_method(D_METHOD("get_string", "bytes"), &StreamPeer::get_string, DEFVAL(-1));
diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h
index 29cdb82615..44bbfbf1d5 100644
--- a/core/io/stream_peer.h
+++ b/core/io/stream_peer.h
@@ -73,6 +73,7 @@ public:
void put_u32(uint32_t p_val);
void put_64(int64_t p_val);
void put_u64(uint64_t p_val);
+ void put_half(float p_val);
void put_float(float p_val);
void put_double(double p_val);
void put_string(const String &p_string);
@@ -87,6 +88,7 @@ public:
int32_t get_32();
uint64_t get_u64();
int64_t get_64();
+ float get_half();
float get_float();
double get_double();
String get_string(int p_bytes = -1);
diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml
index b782937a8a..a8fb648f7b 100644
--- a/doc/classes/FileAccess.xml
+++ b/doc/classes/FileAccess.xml
@@ -173,6 +173,12 @@
Returns the next 32 bits from the file as a floating-point number.
</description>
</method>
+ <method name="get_half" qualifiers="const">
+ <return type="float" />
+ <description>
+ Returns the next 16 bits from the file as a half-precision floating-point number.
+ </description>
+ </method>
<method name="get_hidden_attribute" qualifiers="static">
<return type="bool" />
<param index="0" name="file" type="String" />
@@ -470,6 +476,13 @@
Stores a floating-point number as 32 bits in the file.
</description>
</method>
+ <method name="store_half">
+ <return type="void" />
+ <param index="0" name="value" type="float" />
+ <description>
+ Stores a half-precision floating-point number as 16 bits in the file.
+ </description>
+ </method>
<method name="store_line">
<return type="void" />
<param index="0" name="line" type="String" />
diff --git a/doc/classes/StreamPeer.xml b/doc/classes/StreamPeer.xml
index ad5c5472b8..acff5cf604 100644
--- a/doc/classes/StreamPeer.xml
+++ b/doc/classes/StreamPeer.xml
@@ -59,6 +59,12 @@
Gets a single-precision float from the stream.
</description>
</method>
+ <method name="get_half">
+ <return type="float" />
+ <description>
+ Gets a half-precision float from the stream.
+ </description>
+ </method>
<method name="get_partial_data">
<return type="Array" />
<param index="0" name="bytes" type="int" />
@@ -162,6 +168,13 @@
Puts a single-precision float into the stream.
</description>
</method>
+ <method name="put_half">
+ <return type="void" />
+ <param index="0" name="value" type="float" />
+ <description>
+ Puts a half-precision float into the stream.
+ </description>
+ </method>
<method name="put_partial_data">
<return type="Array" />
<param index="0" name="data" type="PackedByteArray" />
diff --git a/tests/core/io/test_file_access.h b/tests/core/io/test_file_access.h
index a4d3fd1d70..00d33eaf30 100644
--- a/tests/core/io/test_file_access.h
+++ b/tests/core/io/test_file_access.h
@@ -39,7 +39,7 @@ namespace TestFileAccess {
TEST_CASE("[FileAccess] CSV read") {
Ref<FileAccess> f = FileAccess::open(TestUtils::get_data_path("testdata.csv"), FileAccess::READ);
- REQUIRE(!f.is_null());
+ REQUIRE(f.is_valid());
Vector<String> header = f->get_csv_line(); // Default delimiter: ",".
REQUIRE(header.size() == 4);
@@ -107,6 +107,98 @@ TEST_CASE("[FileAccess] Get as UTF-8 String") {
CHECK(s_cr == "Hello darkness\rMy old friend\rI've come to talk\rWith you again\r");
CHECK(s_cr_nocr == "Hello darknessMy old friendI've come to talkWith you again");
}
+
+TEST_CASE("[FileAccess] Get/Store floating point values") {
+ // BigEndian Hex: 0x40490E56
+ // LittleEndian Hex: 0x560E4940
+ float value = 3.1415f;
+
+ SUBCASE("Little Endian") {
+ const String file_path = TestUtils::get_data_path("floating_point_little_endian.bin");
+ const String file_path_new = TestUtils::get_data_path("floating_point_little_endian_new.bin");
+
+ Ref<FileAccess> f = FileAccess::open(file_path, FileAccess::READ);
+ REQUIRE(f.is_valid());
+ CHECK_EQ(f->get_float(), value);
+
+ Ref<FileAccess> fw = FileAccess::open(file_path_new, FileAccess::WRITE);
+ REQUIRE(fw.is_valid());
+ fw->store_float(value);
+ fw->close();
+
+ CHECK_EQ(FileAccess::get_sha256(file_path_new), FileAccess::get_sha256(file_path));
+
+ DirAccess::remove_file_or_error(file_path_new);
+ }
+
+ SUBCASE("Big Endian") {
+ const String file_path = TestUtils::get_data_path("floating_point_big_endian.bin");
+ const String file_path_new = TestUtils::get_data_path("floating_point_big_endian_new.bin");
+
+ Ref<FileAccess> f = FileAccess::open(file_path, FileAccess::READ);
+ REQUIRE(f.is_valid());
+ f->set_big_endian(true);
+ CHECK_EQ(f->get_float(), value);
+
+ Ref<FileAccess> fw = FileAccess::open(file_path_new, FileAccess::WRITE);
+ REQUIRE(fw.is_valid());
+ fw->set_big_endian(true);
+ fw->store_float(value);
+ fw->close();
+
+ CHECK_EQ(FileAccess::get_sha256(file_path_new), FileAccess::get_sha256(file_path));
+
+ DirAccess::remove_file_or_error(file_path_new);
+ }
+}
+
+TEST_CASE("[FileAccess] Get/Store floating point half precision values") {
+ // IEEE 754 half-precision binary floating-point format:
+ // sign exponent (5 bits) fraction (10 bits)
+ // 0 01101 0101010101
+ // BigEndian Hex: 0x3555
+ // LittleEndian Hex: 0x5535
+ float value = 0.33325195f;
+
+ SUBCASE("Little Endian") {
+ const String file_path = TestUtils::get_data_path("half_precision_floating_point_little_endian.bin");
+ const String file_path_new = TestUtils::get_data_path("half_precision_floating_point_little_endian_new.bin");
+
+ Ref<FileAccess> f = FileAccess::open(file_path, FileAccess::READ);
+ REQUIRE(f.is_valid());
+ CHECK_EQ(f->get_half(), value);
+
+ Ref<FileAccess> fw = FileAccess::open(file_path_new, FileAccess::WRITE);
+ REQUIRE(fw.is_valid());
+ fw->store_half(value);
+ fw->close();
+
+ CHECK_EQ(FileAccess::get_sha256(file_path_new), FileAccess::get_sha256(file_path));
+
+ DirAccess::remove_file_or_error(file_path_new);
+ }
+
+ SUBCASE("Big Endian") {
+ const String file_path = TestUtils::get_data_path("half_precision_floating_point_big_endian.bin");
+ const String file_path_new = TestUtils::get_data_path("half_precision_floating_point_big_endian_new.bin");
+
+ Ref<FileAccess> f = FileAccess::open(file_path, FileAccess::READ);
+ REQUIRE(f.is_valid());
+ f->set_big_endian(true);
+ CHECK_EQ(f->get_half(), value);
+
+ Ref<FileAccess> fw = FileAccess::open(file_path_new, FileAccess::WRITE);
+ REQUIRE(fw.is_valid());
+ fw->set_big_endian(true);
+ fw->store_half(value);
+ fw->close();
+
+ CHECK_EQ(FileAccess::get_sha256(file_path_new), FileAccess::get_sha256(file_path));
+
+ DirAccess::remove_file_or_error(file_path_new);
+ }
+}
+
} // namespace TestFileAccess
#endif // TEST_FILE_ACCESS_H
diff --git a/tests/core/io/test_marshalls.h b/tests/core/io/test_marshalls.h
index de8d6e1406..29e713c3a9 100644
--- a/tests/core/io/test_marshalls.h
+++ b/tests/core/io/test_marshalls.h
@@ -90,6 +90,20 @@ TEST_CASE("[Marshalls] Unsigned 64 bit integer decoding") {
CHECK(decode_uint64(arr) == 0x0f123456789abcdef);
}
+TEST_CASE("[Marshalls] Floating point half precision encoding") {
+ uint8_t arr[2];
+
+ // Decimal: 0.33325195
+ // IEEE 754 half-precision binary floating-point format:
+ // sign exponent (5 bits) fraction (10 bits)
+ // 0 01101 0101010101
+ // Hexadecimal: 0x3555
+ unsigned int actual_size = encode_half(0.33325195f, arr);
+ CHECK(actual_size == sizeof(uint16_t));
+ CHECK(arr[0] == 0x55);
+ CHECK(arr[1] == 0x35);
+}
+
TEST_CASE("[Marshalls] Floating point single precision encoding") {
uint8_t arr[4];
@@ -126,6 +140,13 @@ TEST_CASE("[Marshalls] Floating point double precision encoding") {
CHECK(arr[7] == 0x3f);
}
+TEST_CASE("[Marshalls] Floating point half precision decoding") {
+ uint8_t arr[] = { 0x55, 0x35 };
+
+ // See floating point half precision encoding test case for details behind expected values.
+ CHECK(decode_half(arr) == 0.33325195f);
+}
+
TEST_CASE("[Marshalls] Floating point single precision decoding") {
uint8_t arr[] = { 0x00, 0x00, 0x20, 0x3e };
diff --git a/tests/core/io/test_stream_peer.h b/tests/core/io/test_stream_peer.h
index 31bd69edd0..961b4ac070 100644
--- a/tests/core/io/test_stream_peer.h
+++ b/tests/core/io/test_stream_peer.h
@@ -127,6 +127,17 @@ TEST_CASE("[StreamPeer] Get and sets through StreamPeerBuffer") {
CHECK_EQ(spb->get_u64(), value);
}
+ SUBCASE("A half-precision float value") {
+ float value = 3.1415927f;
+ float expected = 3.14062f;
+
+ spb->clear();
+ spb->put_half(value);
+ spb->seek(0);
+
+ CHECK(spb->get_half() == doctest::Approx(expected));
+ }
+
SUBCASE("A float value") {
float value = 42.0f;
@@ -255,6 +266,17 @@ TEST_CASE("[StreamPeer] Get and sets big endian through StreamPeerBuffer") {
CHECK_EQ(spb->get_float(), value);
}
+ SUBCASE("A half-precision float value") {
+ float value = 3.1415927f;
+ float expected = 3.14062f;
+
+ spb->clear();
+ spb->put_half(value);
+ spb->seek(0);
+
+ CHECK(spb->get_half() == doctest::Approx(expected));
+ }
+
SUBCASE("A double value") {
double value = 42.0;
diff --git a/tests/data/floating_point_big_endian.bin b/tests/data/floating_point_big_endian.bin
new file mode 100644
index 0000000000..9534605ce1
--- /dev/null
+++ b/tests/data/floating_point_big_endian.bin
@@ -0,0 +1 @@
+@IV \ No newline at end of file
diff --git a/tests/data/floating_point_little_endian.bin b/tests/data/floating_point_little_endian.bin
new file mode 100644
index 0000000000..8cd66219d8
--- /dev/null
+++ b/tests/data/floating_point_little_endian.bin
@@ -0,0 +1 @@
+VI@ \ No newline at end of file
diff --git a/tests/data/half_precision_floating_point_big_endian.bin b/tests/data/half_precision_floating_point_big_endian.bin
new file mode 100644
index 0000000000..6519f7500a
--- /dev/null
+++ b/tests/data/half_precision_floating_point_big_endian.bin
@@ -0,0 +1 @@
+5U \ No newline at end of file
diff --git a/tests/data/half_precision_floating_point_little_endian.bin b/tests/data/half_precision_floating_point_little_endian.bin
new file mode 100644
index 0000000000..4f748ab1e9
--- /dev/null
+++ b/tests/data/half_precision_floating_point_little_endian.bin
@@ -0,0 +1 @@
+U5 \ No newline at end of file