boardbite
boardbite

Reputation: 259

Extracting decimal part of a float accurately

Note: I am coding the following in the Arduino variant of C++.

From a given float (always with four decimal digits), I wish to extract just the decimal part ("mantissa") as an integer.

So I attempted this method:

void ExtractDecimalPart(float Value) {
  int IntegerPart = (int)(Value);
  int DecimalPart = 10000 * (Value - IntegerPart); //10000 b/c my float values always have exactly 4 decimal places
  Serial.println (DecimalPart);
}

But the above results in the following:

ExtractDecimalPart (1234.5677); //prints 5677
ExtractDecimalPart (1234.5678); //prints 5677
ExtractDecimalPart (1234.5679); //prints 5678

Note that the latter two print out wrong; I'm guessing this is due to floating point precision issues.

What is an economical way to solve the above?

Upvotes: 1

Views: 9343

Answers (3)

phuclv
phuclv

Reputation: 41922

32-bit float has 23 bits of mantissa, so it can hold only about 7 digits of precision. Your example uses 8 digits, so the last one is just "garbage". You must use a higher precision floating-point type (which is double and long double in standard C++).

On Arduino depending on the variant you may have a standard compliant 64-bit double type or not. If double is exactly the same as float you have several solutions:

  • Write or use some double math library.
    • Or if double is just too costly on an 8-bit MCU like that you can create a custom floating-point type like 40-bit or 48-bit.
    • Or you can use float-float arithmetic type like this.
  • Use a fixed-point type. Since you always have 4 decimal digits this may suit you the most for this usecase.
    • You can use a Q26.6 or Q25.7 binary format for better arithmetic performance
    • Or better use a scaled decimal type with the last 4 digits as the fractional part for better decimal math output. In some cases you can also store the last 4 decimal digits and the integer part separately, but be careful with this implementation.

You can find out more information regarding fixed-point in this question, or in tag

Upvotes: 0

blue
blue

Reputation: 2793

The simplest way to avoid problems with floating point precision is to cast your number to a string and then only print what's after the point.

EDIT

I'm not sure how to do it with arduino's version of c++ so feel free to edit my answer to add an example everyone!

Upvotes: 1

John b
John b

Reputation: 1398

This is a cool question.

I will assume that ardiuno is using IEEE 754 when you set a float equal to 1234.5677 the number closest to that that fits in 4 bytes is 1.2345677490234375E3 which looks like 0x449A522B in hex.

but when you put 1234.5678 in to a float the best number it can form is 1.2345677490234375E3 Which is just short. In hex it is 0x449A522B.

So in short floats just can't store number that require number of digites you are using.

Upvotes: 2

Related Questions