Subject: new library

Hi,

im a software developer and i have been studying and writing device drivers on an amateur basis.

I discovered that the float points are not easy to work in kernel land, for an obvious reason: Each CPU has a its owner way dealing with float points in FPU,

and somes CPUs don't have an FPU.

Although, with the kernel_fpu_begin/end, I can do arithmetic with float points....

But i wanted to show this in printk(), and unless im very mistaken this is not possible. What i can do is show a float point as an int number.

For ex: 12.34, changed to 1234. In dmesg output: [123432] 1234.


This is make never implemented, because probably dealing with floating points at the kernel land is extremely rare.

But, in my kernel fork I writed a library to change an integer number, to string with float point.

For ex: 1234 -> "12.34", and i show this in dmesg output: [09876] 12.34


I really don't know if this is useful, but here it is my initial sugestion is


include/linux/int2fpstr.h ++++++++


+ #ifndef _LINUX_INT2FPSTR_H
+ #define _LINUX_INT2FPSTR_H
+
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/string.h>
+
+ inline __kernel_size_t size_alloc(int number)
+ {
+ const int neg = number < 0;
+ if(neg) number *= -1;
+
+ int ret = neg ? 2 : 1;
+ for(;
+ number;
+ number /= 10, ret++
+ );
+
+ return ret;
+ }
+
+ inline void int2fpstr(int number,
+ const int decimal_places, char* dest)
+ {
+ if(!number) {
+ strncpy(dest, "0", 1);
+ return;
+ }
+
+ const __kernel_size_t size_alloc_n = size_alloc(number);
+ char* buffer = kmalloc(size_alloc_n, GFP_KERNEL);
+
+ int buf_index = size_alloc_n;
+ *(buffer + buf_index) = '\0';
+
+ int c_dec_places = 0;
+ int point_include = decimal_places < 1;
+
+ int neg = number < 0;
+ if(neg)
+ number *= -1;
+
+ for (; number && buf_index;
+ --buf_index, number /= 10)
+ {
+ c_dec_places++;
+ if (!point_include
+ && c_dec_places > decimal_places)
+ {
+ *(buffer + buf_index--) = '.';
+ point_include = 1;
+ }
+
+ *(buffer + buf_index) = "0123456789"[number % 10];
+ }
+
+ if(neg)
+ *(buffer + buf_index--) = '-';
+
+ strncpy(dest, &buffer[buf_index+1], size_alloc_n);
+ }
+
+ #endif



so, i create a unitest to library:

lib/int2fpstr_kunit.c ++++++

+ #include <kunit/test.h>
+ #include <linux/slab.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+
+ #include <linux/int2fpstr.h>
+
+ static void should_return_2345_without_errors(struct kunit *test)
+ {
+ char *dest = kmalloc(size_alloc(2345), GFP_KERNEL);
+ int2fpstr(2345, 2, dest);
+ int ret = strncmp(dest, "23.45", 5);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ kfree(dest);
+ }
+
+ static void should_return_2_without_errors_and_float_point(struct kunit *test)
+ {
+ char *dest = kmalloc(size_alloc(2), GFP_KERNEL);
+ int2fpstr(2, 0, dest);
+ int ret = strncmp(dest, "2", 1);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ kfree(dest);
+ }
+
+ static void should_return_0_without_errors(struct kunit *test)
+ {
+ char *dest = kmalloc(size_alloc(1), GFP_KERNEL);
+ int2fpstr(0, 0, dest);
+ int ret = strncmp(dest, "0", 1);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ kfree(dest);
+ }
+
+ static void should_return_12_without_errors_and_float_point(struct kunit *test)
+ {
+ char *dest = kmalloc(size_alloc(12), GFP_KERNEL);
+ int2fpstr(12, 0, dest);
+ int ret = strncmp(dest, "12", 2);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ kfree(dest);
+ }
+
+ static void should_return_12_without_errors(struct kunit *test)
+ {
+ char *dest = kmalloc(size_alloc(12) + 1, GFP_KERNEL);
+ int2fpstr(-12, 0, dest);
+ int ret = strncmp(dest, "-12", 3);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ kfree(dest);
+ }
+
+ static void should_return_125_without_errors(struct kunit *test)
+ {
+ char *dest = kmalloc(size_alloc(125), GFP_KERNEL);
+ int2fpstr(-125, 1, dest);
+ int ret = strncmp(dest, "-12.5", 5);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ kfree(dest);
+ }
+
+ static struct kunit_case int_to_fp_str_test_case[] = {
+ KUNIT_CASE(should_return_12_without_errors_and_float_point),
+ KUNIT_CASE(should_return_0_without_errors),
+ KUNIT_CASE(should_return_2_without_errors_and_float_point),
+ KUNIT_CASE(should_return_2345_without_errors),
+ KUNIT_CASE(should_return_12_without_errors),
+ KUNIT_CASE(should_return_125_without_errors),
+ { /* sentinel */ }
+ };
+
+ static struct kunit_suite int_to_fp_str_test = {
+ .name = "int2fpstr",
+ .test_cases = int_to_fp_str_test_case
+ };
+
+ kunit_test_suite(int_to_fp_str_test);
+
+ MODULE_AUTHOR("Guilherme Giacomo Simoes <[email protected]>");
+ MODULE_LICENSE("GPL");


This library can help the drivers writers, and (perhaps and very rarely) the subsystems writers.



2024-01-12 01:07:29

by Al Viro

[permalink] [raw]
Subject: Re: new library

On Thu, Jan 11, 2024 at 09:48:46PM -0300, Guilherme Gi?como Sim?es wrote:

> For ex: 1234 -> "12.34", and i show this in dmesg output: [09876] 12.34

Er... What's wrong with "%d.%0*d", n / 100, 2, n % 100 in sprintf/printk/etc.
arguments? It's slightly messier if you want to handle the negatives as well,
but also not impossible - "%s%d.%0*d", n < 0 ? "-" : "", abs(n) / 100, 2, abs(n) % 100
will do it, kernel and userland alike. And I'm pretty sure it's going to be cheaper
than your solution...