4

Well, this is not at all an optimization question.

I am writing a (for now) simple Linux kernel module in which I need to find the average of some positions. These positions are stored as floating point (i.e. float) variables. (I am the author of the whole thing, so I can change that, but I'd rather keep the precission of float and not get involved in that if I can avoid it).

Now, these position values are stored (or at least used to) in the kernel simply for storage. One user application writes these data (through shared memory (I am using RTAI, so yes I have shared memory between kernel and user spaces)) and others read from it. I assume read and write from float variables would not use the FPU so this is safe.

By safe, I mean avoiding FPU in the kernel, not to mention some systems may not even have an FPU. I am not going to use kernel_fpu_begin/end, as that likely breaks the real-time-ness of my tasks.

Now in my kernel module, I really don't need much precision (since the positions are averaged anyway), but I would need it up to say 0.001. My question is, how can I portably turn a floating point number to an integer (1000 times the original number) without using the FPU?

I thought about manually extracting the number from the float's bit-pattern, but I'm not sure if it's a good idea as I am not sure how endian-ness affects it, or even if floating points in all architectures are standard.

Community
  • 1
  • 1
Shahbaz
  • 46,337
  • 19
  • 116
  • 182
  • 5
    Why not just store the values as 1000x ints in the first place ? – Paul R Apr 18 '12 at 15:43
  • 1
    @PaulR I think OP thought about that, as he says he can change that but he'd rather keep the precision so he asks if there is another way of doing this. Good question, I'd like to know too, for my personal knowledge. – Eregrith Apr 18 '12 at 15:47
  • @PaulR, that's exactly what I am thinking of doing now as it seems hopeless any other way. I don't want to miss a chance on a nice solution though. – Shahbaz Apr 18 '12 at 15:47
  • @PaulR, also, the main kernel module is providing all these as an API. The module I am writing is a sample, but I wouldn't know if other people who would write other modules in the future would need higher precision or not. Not to mention telling people "this value is actually 1000 times the real value" is not a good idea in my opinion. – Shahbaz Apr 18 '12 at 15:49
  • Why not just pick a suitable fixed point format that would give you all the range and precision you might need, e.g. 16.16 ? – Paul R Apr 18 '12 at 16:02
  • @PaulR, that's even less desirable than the 1000 times solution. With the 1000 times solution, at least you have one number that you can sum/divide, even though it is in a different unit. With a fixed point format I would need two numbers (before and after digit) which are more complicated to handle. Anyway, I think I would stick with the 1000 times solution. Just append `_mm` at the end of variable name to show it's in millimeters! – Shahbaz Apr 18 '12 at 16:36
  • 1
    Fixed point numbers are very easy to sum and divide - you don't even need to separate out the integer and fractional parts. – Paul R Apr 18 '12 at 16:38
  • 1
    For addition, subtraction, and also multiply/divide by an integer, you just treat fixed point numbers as if they were ints. You only need to deal with the implicit decimal point when multiplying/dividing two fixed point numbers, and even then it's just an additional shift. – Paul R Apr 18 '12 at 17:01

4 Answers4

5

If you want to tell gcc to use a software floating point library there's apparently a switch for that, albeit perhaps not turnkey in the standard environment:

Using software floating point on x86 linux

In fact, this article suggests that linux kernel and its modules are already compiled with -msoft-float:

http://www.linuxsmiths.com/blog/?p=253

That said, @PaulR's suggestion seems most sensible. And if you offer an API which does whatever conversions you like then I don't see why it's any uglier than anything else.

Community
  • 1
  • 1
  • I had seen in the same article that -msoft-float is enabled when building kernel modules. I had got undefined references to __div.. for 64 bit division before, but not floating point, so I thought this should be the same (floating point division uses external function but not addition). Didn't test it though. Still, with 64 bit division, there is `do_div`, but for floating point numbers I believe no such facilities exist. – Shahbaz Apr 18 '12 at 16:31
  • Does it find `__fixsfdi` by any chance? http://gcc.gnu.org/onlinedocs/gccint/Soft-float-library-routines.html – HostileFork says dont trust SE Apr 18 '12 at 16:50
  • I'm going to accept your answer (but after some time in case other answers show up), even though I would probably change floats to `long long`s with fixed precision. – Shahbaz Apr 18 '12 at 16:52
5

The SoftFloat software package has the function float32_to_int32 that does exactly what you want (it implements IEEE 754 in software).

In the end it will be useful to have some sort of floating point support in a kernel anyway (be it hardware or software), so including this in your project would most likely be a wise decision. It's not too big either.

orlp
  • 112,504
  • 36
  • 218
  • 315
  • This is interesting. I need to ask one thing though. Is it sure that all FPUs out there (at least, on all computers capable of running a linux kernel) using IEEE 754 standard? – Shahbaz Apr 18 '12 at 16:32
  • @Shazbaz: No, the linux kernel supports more architectures than just IEEE 754 ones, but supportign IEEE 754 is a good start (almost all architectures use it nowadays, notable exception being IBM mainframes). – orlp Apr 18 '12 at 18:50
2

Really, I think you should just change your module's API to use data that's already in integer format, if possible. Having floating point types in a kernel-user interface is just a bad idea when you're not allowed to use floating point in kernelspace.

With that said, if you're using single-precision float, it's essentially ALWAYS going to be IEEE 754 single precision, and the endianness should match the integer endianness. As far as I know this is true for all archs Linux supports. With that in mind, just treat them as unsigned 32-bit integers and extract the bits to scale them. I would scale by 1024 rather than 1000 if possible; doing that is really easy. Just start with the mantissa bits (bits 0-22), "or" on bit 23, then right shift if the exponent (after subtracting the bias of 127) is less than 23 and left shift if it's greater than 23. You'll need to handle the cases where the right shift amount is greater than 32 (which C wouldn't allow; you have to just special-case the zero result) or where the left shift is sufficiently large to overflow (in which case you'll probably want to clamp the output).

If you happen to know your values won't exceed a particular range, of course, you might be able to eliminate some of these checks. In fact, if your values never exceed 1 and you can pick the scaling, you could pick it to be 2^23 and then you could just use ((float_bits & 0x7fffff)|0x800000) directly as the value when the exponent is zero, and otherwise right-shift.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
0

You can use rational numbers instead of floats. The operations (multiplication, addition) can be implemented without loss in accuracy too.

If you really only need 1/1000 precision, you can just store x*1000 as a long integer.

j13r
  • 2,576
  • 2
  • 21
  • 28